237 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package server
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"coopcloud.tech/abra/cli/formatter"
 | 
						|
	"coopcloud.tech/abra/cli/internal"
 | 
						|
	"coopcloud.tech/libcapsul"
 | 
						|
	"github.com/AlecAivazis/survey/v2"
 | 
						|
	"github.com/hetznercloud/hcloud-go/hcloud"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/urfave/cli/v2"
 | 
						|
)
 | 
						|
 | 
						|
func newHetznerCloudVPS(c *cli.Context) error {
 | 
						|
	if err := internal.EnsureNewHetznerCloudVPSFlags(c); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken))
 | 
						|
 | 
						|
	var sshKeysRaw []string
 | 
						|
	var sshKeys []*hcloud.SSHKey
 | 
						|
	for _, sshKey := range c.StringSlice("hetzner-ssh-keys") {
 | 
						|
		if sshKey == "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		sshKey, _, err := client.SSHKey.GetByName(c.Context, sshKey)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		sshKeys = append(sshKeys, sshKey)
 | 
						|
		sshKeysRaw = append(sshKeysRaw, sshKey.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	serverOpts := hcloud.ServerCreateOpts{
 | 
						|
		Name:       internal.HetznerCloudName,
 | 
						|
		ServerType: &hcloud.ServerType{Name: internal.HetznerCloudType},
 | 
						|
		Image:      &hcloud.Image{Name: internal.HetznerCloudImage},
 | 
						|
		SSHKeys:    sshKeys,
 | 
						|
		Location:   &hcloud.Location{Name: internal.HetznerCloudLocation},
 | 
						|
	}
 | 
						|
 | 
						|
	tableColumns := []string{"name", "type", "image", "ssh-keys", "location"}
 | 
						|
	table := formatter.CreateTable(tableColumns)
 | 
						|
	table.Append([]string{
 | 
						|
		internal.HetznerCloudName,
 | 
						|
		internal.HetznerCloudType,
 | 
						|
		internal.HetznerCloudImage,
 | 
						|
		strings.Join(sshKeysRaw, "\n"),
 | 
						|
		internal.HetznerCloudLocation,
 | 
						|
	})
 | 
						|
	table.Render()
 | 
						|
 | 
						|
	response := false
 | 
						|
	prompt := &survey.Confirm{
 | 
						|
		Message: "continue with hetzner cloud VPS creation?",
 | 
						|
	}
 | 
						|
 | 
						|
	if err := survey.AskOne(prompt, &response); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if !response {
 | 
						|
		logrus.Fatal("exiting as requested")
 | 
						|
	}
 | 
						|
 | 
						|
	res, _, err := client.Server.Create(c.Context, serverOpts)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var rootPassword string
 | 
						|
	if len(sshKeys) > 0 {
 | 
						|
		rootPassword = "N/A (using SSH keys)"
 | 
						|
	} else {
 | 
						|
		rootPassword = res.RootPassword
 | 
						|
	}
 | 
						|
 | 
						|
	ip := res.Server.PublicNet.IPv4.IP.String()
 | 
						|
 | 
						|
	fmt.Println(fmt.Sprintf(`
 | 
						|
Your new Hetzner Cloud VPS has successfully been created! Here are the details:
 | 
						|
 | 
						|
    name: %s
 | 
						|
    IP address: %s
 | 
						|
    root password: %s
 | 
						|
 | 
						|
You can access this new VPS via SSH using the following command:
 | 
						|
 | 
						|
    ssh root@%s
 | 
						|
 | 
						|
Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
 | 
						|
not list this server)! You will need to assign a domain name record ("abra
 | 
						|
record new") and add the server to your Abra configuration ("abra server add")
 | 
						|
to have a working server that you can deploy Co-op Cloud apps to.
 | 
						|
	`,
 | 
						|
		internal.HetznerCloudName, ip, rootPassword,
 | 
						|
		ip,
 | 
						|
	))
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func newCapsulVPS(c *cli.Context) error {
 | 
						|
	if err := internal.EnsureNewCapsulVPSFlags(c); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", internal.CapsulInstanceURL)
 | 
						|
 | 
						|
	var sshKeys []string
 | 
						|
	for _, sshKey := range c.StringSlice("capsul-ssh-keys") {
 | 
						|
		if sshKey == "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		sshKeys = append(sshKeys, sshKey)
 | 
						|
	}
 | 
						|
 | 
						|
	tableColumns := []string{"instance", "name", "type", "image", "ssh-keys"}
 | 
						|
	table := formatter.CreateTable(tableColumns)
 | 
						|
	table.Append([]string{
 | 
						|
		internal.CapsulInstanceURL,
 | 
						|
		internal.CapsulName,
 | 
						|
		internal.CapsulType,
 | 
						|
		internal.CapsulImage,
 | 
						|
		strings.Join(sshKeys, "\n"),
 | 
						|
	})
 | 
						|
	table.Render()
 | 
						|
 | 
						|
	response := false
 | 
						|
	prompt := &survey.Confirm{
 | 
						|
		Message: "continue with capsul creation?",
 | 
						|
	}
 | 
						|
 | 
						|
	if err := survey.AskOne(prompt, &response); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if !response {
 | 
						|
		logrus.Fatal("exiting as requested")
 | 
						|
	}
 | 
						|
 | 
						|
	capsulClient := libcapsul.New(capsulCreateURL, internal.CapsulAPIToken)
 | 
						|
	resp, err := capsulClient.Create(
 | 
						|
		internal.CapsulName,
 | 
						|
		internal.CapsulType,
 | 
						|
		internal.CapsulImage,
 | 
						|
		sshKeys,
 | 
						|
	)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Println(fmt.Sprintf(`
 | 
						|
Your new Capsul has successfully been created! Here are the details:
 | 
						|
 | 
						|
    Capsul name: %s
 | 
						|
    Capsul ID:   %v
 | 
						|
 | 
						|
You will need to log into your Capsul instance web interface to retrieve the IP
 | 
						|
address. You can learn all about how to get SSH access to your new Capsul on:
 | 
						|
 | 
						|
    %s/about-ssh
 | 
						|
 | 
						|
Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
 | 
						|
not list this server)! You will need to assign a domain name record ("abra
 | 
						|
record new") and add the server to your Abra configuration ("abra server add")
 | 
						|
to have a working server that you can deploy Co-op Cloud apps to.
 | 
						|
	`, internal.CapsulName, resp.ID, internal.CapsulInstanceURL))
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
var serverNewCommand = &cli.Command{
 | 
						|
	Name:    "new",
 | 
						|
	Aliases: []string{"n"},
 | 
						|
	Usage:   "Create a new server using a 3rd party provider",
 | 
						|
	Description: `
 | 
						|
This command creates a new server via a 3rd party provider.
 | 
						|
 | 
						|
The following providers are supported:
 | 
						|
 | 
						|
		Capsul https://git.cyberia.club/Cyberia/capsul-flask
 | 
						|
    Hetzner Cloud https://docs.hetzner.com/cloud
 | 
						|
 | 
						|
You may invoke this command in "wizard" mode and be prompted for input:
 | 
						|
 | 
						|
    abra record new
 | 
						|
 | 
						|
API tokens are read from the environment if specified, e.g.
 | 
						|
 | 
						|
    export HCLOUD_TOKEN=...
 | 
						|
 | 
						|
Where "$provider_TOKEN" is the expected env var format.
 | 
						|
`,
 | 
						|
	ArgsUsage: "<provider>",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.ServerProviderFlag,
 | 
						|
 | 
						|
		// Capsul
 | 
						|
		internal.CapsulInstanceURLFlag,
 | 
						|
		internal.CapsulTypeFlag,
 | 
						|
		internal.CapsulImageFlag,
 | 
						|
		internal.CapsulSSHKeysFlag,
 | 
						|
		internal.CapsulAPITokenFlag,
 | 
						|
 | 
						|
		// Hetzner
 | 
						|
		internal.HetznerCloudNameFlag,
 | 
						|
		internal.HetznerCloudTypeFlag,
 | 
						|
		internal.HetznerCloudImageFlag,
 | 
						|
		internal.HetznerCloudSSHKeysFlag,
 | 
						|
		internal.HetznerCloudLocationFlag,
 | 
						|
		internal.HetznerCloudAPITokenFlag,
 | 
						|
	},
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		if err := internal.EnsureServerProvider(); err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		switch internal.ServerProvider {
 | 
						|
		case "capsul":
 | 
						|
			if err := newCapsulVPS(c); err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
		case "hetzner-cloud":
 | 
						|
			if err := newHetznerCloudVPS(c); err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 |