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: "", Action: func(c *cli.Context) error { if len(c.Args()) > 0 && local || !internal.ValidateSubCmdFlags(c) { err := errors.New("cannot use 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 }, }