forked from coop-cloud/abra
262 lines
6.4 KiB
Go
262 lines
6.4 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/formatter"
|
|
"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},
|
|
}
|
|
|
|
sshKeyIDs := strings.Join(sshKeysRaw, "\n")
|
|
if sshKeyIDs == "" {
|
|
sshKeyIDs = "N/A (password auth)"
|
|
}
|
|
|
|
tableColumns := []string{"name", "type", "image", "ssh-keys", "location"}
|
|
table := formatter.CreateTable(tableColumns)
|
|
table.Append([]string{
|
|
internal.HetznerCloudName,
|
|
internal.HetznerCloudType,
|
|
internal.HetznerCloudImage,
|
|
sshKeyIDs,
|
|
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.
|
|
|
|
When setting up domain name records, you probably want to set up the following
|
|
2 A records. This supports deploying apps to your root domain (e.g.
|
|
example.com) and other apps on sub-domains (e.g. foo.example.com,
|
|
bar.example.com).
|
|
|
|
@ 1800 IN A %s
|
|
* 1800 IN A %s
|
|
|
|
"abra record new --auto" can help you do this quickly if you use a supported
|
|
DNS provider.
|
|
|
|
`,
|
|
internal.HetznerCloudName, ip, rootPassword,
|
|
ip, ip, 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.
|
|
|
|
When setting up domain name records, you probably want to set up the following
|
|
2 A records. This supports deploying apps to your root domain (e.g.
|
|
example.com) and other apps on sub-domains (e.g. foo.example.com,
|
|
bar.example.com).
|
|
|
|
@ 1800 IN A <your-capsul-ip>
|
|
* 1800 IN A <your-capsul-ip>
|
|
|
|
`, 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.
|
|
`,
|
|
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
|
|
},
|
|
}
|