From 5294e84d5e7b79cf70f72727716d3535c32f7dd4 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Mon, 2 Aug 2021 15:11:14 +0200 Subject: [PATCH] feat: implement capsul create --- .envrc.sample | 4 +- TODO.md | 4 +- cli/server/new.go | 130 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 132 insertions(+), 6 deletions(-) diff --git a/.envrc.sample b/.envrc.sample index fb4427e4..aeae2432 100644 --- a/.envrc.sample +++ b/.envrc.sample @@ -1,5 +1,3 @@ -# The path to our pass credentials store export PASSWORD_STORE_DIR=$(pwd)/../../autonomic/passwords/passwords/ - -# The Hetzner Cloud API token for managing our instances export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key) +# export CAPSUL_TOKEN=... diff --git a/TODO.md b/TODO.md index ca2a92b5..efcd1b85 100644 --- a/TODO.md +++ b/TODO.md @@ -5,11 +5,11 @@ ## Feature parity - [ ] Commands - - [ ] `abra server` + - [x] `abra server` - [x] `ls` - [x] `add` - [ ] `new` - - [ ] `capsul` + - [x] `capsul` - [x] `hetzner` - [x] `rm` - [x] `init` diff --git a/cli/server/new.go b/cli/server/new.go index c7de60e0..adfe96ab 100644 --- a/cli/server/new.go +++ b/cli/server/new.go @@ -1,7 +1,13 @@ package server import ( + "bytes" "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" "coopcloud.tech/abra/cli/formatter" "github.com/hetznercloud/hcloud-go/hcloud" @@ -71,7 +77,7 @@ environment variable or otherwise passing the "--env/-e" flag. } if hetznerCloudAPIToken == "" { - logrus.Fatal("API token is missing, cannot continue") + logrus.Fatal("Hetznew Cloud API token is missing, cannot continue") } ctx := context.Background() @@ -111,6 +117,127 @@ environment variable or otherwise passing the "--env/-e" flag. }, } +var capsulInstance string +var capsulType string +var capsulImage string +var capsulSSHKey string +var capsulAPIToken string +var serverNewCapsulCommand = &cli.Command{ + Name: "capsul", + Usage: "Create a new Capsul virtual server", + ArgsUsage: "", + Description: ` +Create a new Capsul virtual server. + +This command uses the uses the Capsul API bindings of your chosen instance to +send a server creation request. You must already have an account on your chosen +Capsul instance before using this command. + +Your token can be loaded from the environment using the CAPSUL_TOKEN +environment variable or otherwise passing the "--env/-e" flag. +`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "instance", + Aliases: []string{"I"}, + Usage: "Capsul instance", + Destination: &capsulInstance, + Value: "yolo.servers.coop", + }, + &cli.StringFlag{ + Name: "type", + Aliases: []string{"t"}, + Usage: "Server type", + Value: "f1-xs", + Destination: &capsulType, + }, + &cli.StringFlag{ + Name: "image", + Aliases: []string{"i"}, + Usage: "Image type", + Value: "debian10", + Destination: &capsulImage, + }, + &cli.StringFlag{ + Name: "ssh-key", + Aliases: []string{"s"}, + Usage: "SSH key", + Value: "", + Destination: &capsulSSHKey, + }, + &cli.StringFlag{ + Name: "token", + Aliases: []string{"T"}, + Usage: "Capsul instance API token", + EnvVars: []string{"CAPSUL_TOKEN"}, + Destination: &capsulAPIToken, + }, + }, + Action: func(c *cli.Context) error { + name := c.Args().First() + if name == "" { + return cli.ShowSubcommandHelp(c) + } + + if capsulAPIToken == "" { + logrus.Fatal("Capsul API token is missing, cannot continue") + } + + // yep, the response time is quite slow, something to fix Capsul side + client := &http.Client{Timeout: 20 * time.Second} + + capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", capsulInstance) + values := map[string]string{ + "name": name, + "size": capsulType, + "os": capsulImage, + "ssh_key_0": capsulSSHKey, + } + + payload, err := json.Marshal(values) + if err != nil { + logrus.Fatal(err) + } + + req, err := http.NewRequest("POST", capsulCreateURL, bytes.NewBuffer(payload)) + if err != nil { + logrus.Fatal(err) + } + + req.Header = http.Header{ + "Content-Type": []string{"application/json"}, + "Authorization": []string{capsulAPIToken}, + } + + res, err := client.Do(req) + if err != nil { + logrus.Fatal(err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + body, err := ioutil.ReadAll(res.Body) + if err != nil { + panic(err) + } + logrus.Fatal(string(body)) + } + + type capsulCreateResponse struct{ ID string } + var resp capsulCreateResponse + if err := json.NewDecoder(res.Body).Decode(&resp); err != nil { + logrus.Fatal(err) + } + + tableColumns := []string{"Name", "ID"} + table := formatter.CreateTable(tableColumns) + table.Append([]string{name, resp.ID}) + table.Render() + + return nil + }, +} + var serverNewCommand = &cli.Command{ Name: "new", Usage: "Create a new server using a 3rd party provider", @@ -118,5 +245,6 @@ var serverNewCommand = &cli.Command{ ArgsUsage: "", Subcommands: []*cli.Command{ serverNewHetznerCloudCommand, + serverNewCapsulCommand, }, }