forked from toolshed/abra
.gitea
cli
app
catalogue
internal
recipe
record
server
add.go
list.go
new.go
prune.go
remove.go
server.go
updater
cli.go
cmd
pkg
scripts
tests
.dockerignore
.drone.yml
.e2e.env.sample
.envrc.sample
.gitignore
.goreleaser.yml
AUTHORS.md
Dockerfile
LICENSE
Makefile
README.md
go.mod
go.sum
renovate.json
Closes coop-cloud/organising#389. Closes coop-cloud/organising#341. Closes coop-cloud/organising#326. Closes coop-cloud/organising#380. Closes coop-cloud/organising#360.
182 lines
4.6 KiB
Go
182 lines
4.6 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/client"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
contextPkg "coopcloud.tech/abra/pkg/context"
|
|
"coopcloud.tech/abra/pkg/dns"
|
|
"coopcloud.tech/abra/pkg/server"
|
|
sshPkg "coopcloud.tech/abra/pkg/ssh"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var local bool
|
|
var localFlag = &cli.BoolFlag{
|
|
Name: "local, l",
|
|
Usage: "Use local server",
|
|
Destination: &local,
|
|
}
|
|
|
|
func cleanUp(domainName string) {
|
|
if domainName != "default" {
|
|
logrus.Infof("cleaning up context for %s", domainName)
|
|
if err := client.DeleteContext(domainName); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
logrus.Infof("attempting to clean up server directory for %s", domainName)
|
|
|
|
serverDir := filepath.Join(config.SERVERS_DIR, domainName)
|
|
files, err := config.GetAllFilesInDirectory(serverDir)
|
|
if err != nil {
|
|
logrus.Fatalf("unable to list files in %s: %s", serverDir, err)
|
|
}
|
|
|
|
if len(files) > 0 {
|
|
logrus.Warnf("aborting clean up of %s because it is not empty", serverDir)
|
|
return
|
|
}
|
|
|
|
if err := os.RemoveAll(serverDir); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// newContext creates a new internal Docker context for a server. This is how
|
|
// Docker manages SSH connection details. These are stored to disk in
|
|
// ~/.docker. Abra can manage this completely for the user, so it's an
|
|
// implementation detail.
|
|
func newContext(c *cli.Context, domainName, username, port string) error {
|
|
store := contextPkg.NewDefaultDockerContextStore()
|
|
contexts, err := store.Store.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, context := range contexts {
|
|
if context.Name == domainName {
|
|
logrus.Debugf("context for %s already exists", domainName)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
logrus.Debugf("creating context with domain %s, username %s and port %s", domainName, username, port)
|
|
|
|
if err := client.CreateContext(domainName, username, port); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createServerDir creates the ~/.abra/servers/... directory for a new server.
|
|
func createServerDir(domainName string) error {
|
|
if err := server.CreateServerDir(domainName); err != nil {
|
|
if !os.IsExist(err) {
|
|
return err
|
|
}
|
|
logrus.Debugf("server dir for %s already created", domainName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var serverAddCommand = cli.Command{
|
|
Name: "add",
|
|
Aliases: []string{"a"},
|
|
Usage: "Add a server to your configuration",
|
|
Description: `
|
|
Add a new server to your configuration so that it can be managed by Abra.
|
|
|
|
Abra uses the SSH command-line to discover connection details for your server.
|
|
It is advised to configure an entry per-host in your ~/.ssh/config for each
|
|
server. For example:
|
|
|
|
Host example.com
|
|
Hostname example.com
|
|
User exampleUser
|
|
Port 12345
|
|
IdentityFile ~/.ssh/example@somewhere
|
|
|
|
Abra can then load SSH connection details from this configuratiion with:
|
|
|
|
abra server add example.com
|
|
|
|
If "--local" is passed, then Abra assumes that the current local server is
|
|
intended as the target server. This is useful when you want to have your entire
|
|
Co-op Cloud config located on the server itself, and not on your local
|
|
developer machine.
|
|
`,
|
|
Flags: []cli.Flag{
|
|
internal.DebugFlag,
|
|
internal.NoInputFlag,
|
|
localFlag,
|
|
},
|
|
Before: internal.SubCommandBefore,
|
|
ArgsUsage: "<domain>",
|
|
Action: func(c *cli.Context) error {
|
|
if len(c.Args()) > 0 && local || !internal.ValidateSubCmdFlags(c) {
|
|
err := errors.New("cannot use <domain> and --local together")
|
|
internal.ShowSubcommandHelpAndError(c, err)
|
|
}
|
|
|
|
var domainName string
|
|
if local {
|
|
domainName = "default"
|
|
} else {
|
|
domainName = internal.ValidateDomain(c)
|
|
}
|
|
|
|
if local {
|
|
if err := createServerDir(domainName); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Infof("attempting to create client for %s", domainName)
|
|
if _, err := client.New(domainName); err != nil {
|
|
cleanUp(domainName)
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Info("local server added")
|
|
|
|
return nil
|
|
}
|
|
|
|
if _, err := dns.EnsureIPv4(domainName); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := createServerDir(domainName); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
hostConfig, err := sshPkg.GetHostConfig(domainName)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := newContext(c, domainName, hostConfig.User, hostConfig.Port); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Infof("attempting to create client for %s", domainName)
|
|
if _, err := client.New(domainName); err != nil {
|
|
cleanUp(domainName)
|
|
logrus.Debugf("failed to construct client for %s, saw %s", domainName, err.Error())
|
|
logrus.Fatal(sshPkg.Fatal(domainName, err))
|
|
}
|
|
|
|
logrus.Infof("%s added", domainName)
|
|
|
|
return nil
|
|
},
|
|
}
|