forked from toolshed/abra
refactor: better SSH connection details handling
This commit is contained in:
parent
f9e2d24550
commit
9e0d77d5c6
@ -24,18 +24,16 @@ var recipeNewCommand = &cli.Command{
|
|||||||
directory := path.Join(config.APPS_DIR, recipe.Name)
|
directory := path.Join(config.APPS_DIR, recipe.Name)
|
||||||
if _, err := os.Stat(directory); !os.IsNotExist(err) {
|
if _, err := os.Stat(directory); !os.IsNotExist(err) {
|
||||||
logrus.Fatalf("'%s' recipe directory already exists?", directory)
|
logrus.Fatalf("'%s' recipe directory already exists?", directory)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL)
|
url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL)
|
||||||
if err := git.Clone(directory, url); err != nil {
|
if err := git.Clone(directory, url); err != nil {
|
||||||
return err
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gitRepo := path.Join(config.APPS_DIR, recipe.Name, ".git")
|
gitRepo := path.Join(config.APPS_DIR, recipe.Name, ".git")
|
||||||
if err := os.RemoveAll(gitRepo); err != nil {
|
if err := os.RemoveAll(gitRepo); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
logrus.Debugf("removed git repo in '%s'", gitRepo)
|
logrus.Debugf("removed git repo in '%s'", gitRepo)
|
||||||
|
|
||||||
@ -48,13 +46,11 @@ var recipeNewCommand = &cli.Command{
|
|||||||
file, err := os.OpenFile(path, os.O_RDWR, 0755)
|
file, err := os.OpenFile(path, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tpl, err := template.ParseFiles(path)
|
tpl, err := template.ParseFiles(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ask for description and probably other things so that the
|
// TODO: ask for description and probably other things so that the
|
||||||
@ -65,7 +61,6 @@ var recipeNewCommand = &cli.Command{
|
|||||||
Description string
|
Description string
|
||||||
}{recipe.Name, "TODO"}); err != nil {
|
}{recipe.Name, "TODO"}); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,21 +8,43 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/server"
|
"coopcloud.tech/abra/pkg/server"
|
||||||
"coopcloud.tech/abra/pkg/ssh"
|
"coopcloud.tech/abra/pkg/ssh"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
|
"github.com/sfreiberg/simplessh"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dockerInstallMsg = `
|
||||||
|
A docker installation cannot be found on %s. This is a required system dependency
|
||||||
|
for running Co-op Cloud on your server. If you would like, Abra can attempt to install
|
||||||
|
Docker for you using the upstream non-interactive installation script.
|
||||||
|
|
||||||
|
See the following documentation for more:
|
||||||
|
|
||||||
|
https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script
|
||||||
|
|
||||||
|
N.B Docker doesn't recommend it for production environments but many use it for
|
||||||
|
such purposes. Docker stable is now installed by default by this script. The
|
||||||
|
source for this script can be seen here:
|
||||||
|
|
||||||
|
https://github.com/docker/docker-install
|
||||||
|
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
var local bool
|
var local bool
|
||||||
var localFlag = &cli.BoolFlag{
|
var localFlag = &cli.BoolFlag{
|
||||||
Name: "local",
|
Name: "local",
|
||||||
@ -68,6 +90,40 @@ var traefikFlag = &cli.BoolFlag{
|
|||||||
Destination: &traefik,
|
Destination: &traefik,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanUp(domainName string) {
|
||||||
|
logrus.Warnf("cleaning up context for %s", domainName)
|
||||||
|
if err := client.DeleteContext(domainName); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Warnf("cleaning up server directory for %s", domainName)
|
||||||
|
if err := os.RemoveAll(filepath.Join(config.ABRA_SERVER_FOLDER, domainName)); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installDockerLocal(c *cli.Context) error {
|
||||||
|
fmt.Println(fmt.Sprintf(dockerInstallMsg, "this local server"))
|
||||||
|
|
||||||
|
response := false
|
||||||
|
prompt := &survey.Confirm{
|
||||||
|
Message: fmt.Sprintf("attempt install docker on local server?"),
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt, &response); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !response {
|
||||||
|
logrus.Fatal("exiting as requested")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("bash", "-c", "curl -s https://get.docker.com | bash")
|
||||||
|
if err := internal.RunCmd(cmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newLocalServer(c *cli.Context, domainName string) error {
|
func newLocalServer(c *cli.Context, domainName string) error {
|
||||||
if err := createServerDir(domainName); err != nil {
|
if err := createServerDir(domainName); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -80,7 +136,7 @@ func newLocalServer(c *cli.Context, domainName string) error {
|
|||||||
|
|
||||||
if _, err := exec.LookPath("docker"); err != nil {
|
if _, err := exec.LookPath("docker"); err != nil {
|
||||||
if provision {
|
if provision {
|
||||||
if err := installDocker(c, cl, domainName); err != nil {
|
if err := installDockerLocal(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := initSwarm(c, cl, domainName); err != nil {
|
if err := initSwarm(c, cl, domainName); err != nil {
|
||||||
@ -104,21 +160,7 @@ func newLocalServer(c *cli.Context, domainName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContext(c *cli.Context, domainName string) error {
|
func newContext(c *cli.Context, domainName, username, port string) error {
|
||||||
username := c.Args().Get(1)
|
|
||||||
if username == "" {
|
|
||||||
systemUser, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
username = systemUser.Username
|
|
||||||
}
|
|
||||||
|
|
||||||
port := c.Args().Get(2)
|
|
||||||
if port == "" {
|
|
||||||
port = "22"
|
|
||||||
}
|
|
||||||
|
|
||||||
store := client.NewDefaultDockerContextStore()
|
store := client.NewDefaultDockerContextStore()
|
||||||
contexts, err := store.Store.List()
|
contexts, err := store.Store.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -144,48 +186,19 @@ func newContext(c *cli.Context, domainName string) error {
|
|||||||
func newClient(c *cli.Context, domainName string) (*dockerClient.Client, error) {
|
func newClient(c *cli.Context, domainName string) (*dockerClient.Client, error) {
|
||||||
cl, err := client.New(domainName)
|
cl, err := client.New(domainName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warn("cleaning up context due to connection failure")
|
|
||||||
if err := client.DeleteContext(domainName); err != nil {
|
|
||||||
return &dockerClient.Client{}, err
|
|
||||||
}
|
|
||||||
return &dockerClient.Client{}, err
|
return &dockerClient.Client{}, err
|
||||||
}
|
}
|
||||||
return cl, nil
|
return cl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installDocker(c *cli.Context, cl *dockerClient.Client, domainName string) error {
|
func installDocker(c *cli.Context, cl *dockerClient.Client, sshCl *simplessh.Client, domainName string) error {
|
||||||
logrus.Debugf("attempting to construct SSH client for %s", domainName)
|
result, err := sshCl.Exec("which docker")
|
||||||
|
|
||||||
client, err := ssh.New(domainName, sshAuth)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
logrus.Debugf("successfully created SSH client for %s", domainName)
|
|
||||||
|
|
||||||
result, err := client.Exec("which docker")
|
|
||||||
if err != nil && string(result) != "" {
|
if err != nil && string(result) != "" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(result) == "" {
|
if string(result) == "" {
|
||||||
fmt.Println(fmt.Sprintf(`
|
fmt.Println(fmt.Sprintf(dockerInstallMsg, domainName))
|
||||||
A docker installation cannot be found on %s. This is a required system dependency
|
|
||||||
for running Co-op Cloud on your server. If you would like, Abra can attempt to install
|
|
||||||
Docker for you using the upstream non-interactive installation script.
|
|
||||||
|
|
||||||
See the following documentation for more:
|
|
||||||
|
|
||||||
https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script
|
|
||||||
|
|
||||||
N.B Docker doesn't recommend it for production environments but many use it for
|
|
||||||
such purposes. Docker stable is now installed by default by this script. The
|
|
||||||
source for this script can be seen here:
|
|
||||||
|
|
||||||
https://github.com/docker/docker-install
|
|
||||||
|
|
||||||
`, domainName))
|
|
||||||
|
|
||||||
response := false
|
response := false
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
@ -209,13 +222,13 @@ source for this script can be seen here:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Debugf("running '%s' on %s now with sudo password", cmd, domainName)
|
logrus.Debugf("running '%s' on %s now with sudo password", cmd, domainName)
|
||||||
_, err := client.ExecSudo(cmd, sudoPass)
|
_, err := sshCl.ExecSudo(cmd, sudoPass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("running '%s' on %s now without sudo password", cmd, domainName)
|
logrus.Debugf("running '%s' on %s now without sudo password", cmd, domainName)
|
||||||
_, err := client.Exec(cmd)
|
_, err := sshCl.Exec(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -378,11 +391,25 @@ You may omit flags to avoid performing this provisioning logic.
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
username := c.Args().Get(1)
|
||||||
|
if username == "" {
|
||||||
|
systemUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
username = systemUser.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
port := c.Args().Get(2)
|
||||||
|
if port == "" {
|
||||||
|
port = "22"
|
||||||
|
}
|
||||||
|
|
||||||
if err := createServerDir(domainName); err != nil {
|
if err := createServerDir(domainName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := newContext(c, domainName); err != nil {
|
if err := newContext(c, domainName, username, port); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +419,15 @@ You may omit flags to avoid performing this provisioning logic.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if provision {
|
if provision {
|
||||||
if err := installDocker(c, cl, domainName); err != nil {
|
logrus.Debugf("attempting to construct SSH client for %s", domainName)
|
||||||
|
sshCl, err := ssh.New(domainName, sshAuth, username, port)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
defer sshCl.Close()
|
||||||
|
logrus.Debugf("successfully created SSH client for %s", domainName)
|
||||||
|
|
||||||
|
if err := installDocker(c, cl, sshCl, domainName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := initSwarm(c, cl, domainName); err != nil {
|
if err := initSwarm(c, cl, domainName); err != nil {
|
||||||
|
@ -19,29 +19,32 @@ type HostConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetHostConfig retrieves a ~/.ssh/config config for a host.
|
// GetHostConfig retrieves a ~/.ssh/config config for a host.
|
||||||
func GetHostConfig(hostname string, hasIdentityFile bool) (HostConfig, error) {
|
func GetHostConfig(hostname, username, port string, hasIdentityFile bool) (HostConfig, error) {
|
||||||
var hostConfig HostConfig
|
var hostConfig HostConfig
|
||||||
|
|
||||||
var host, sshUser, port, idf string
|
var host, idf string
|
||||||
|
|
||||||
if host = ssh_config.Get(hostname, "Hostname"); host == "" {
|
if host = ssh_config.Get(hostname, "Hostname"); host == "" {
|
||||||
logrus.Debugf("no hostname found in SSH config, assuming %s", hostname)
|
logrus.Debugf("no hostname found in SSH config, assuming %s", hostname)
|
||||||
host = hostname
|
host = hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
if sshUser = ssh_config.Get(hostname, "User"); sshUser == "" {
|
if username == "" {
|
||||||
systemUser, err := user.Current()
|
if username = ssh_config.Get(hostname, "User"); username == "" {
|
||||||
if err != nil {
|
systemUser, err := user.Current()
|
||||||
return hostConfig, err
|
if err != nil {
|
||||||
|
return hostConfig, err
|
||||||
|
}
|
||||||
|
logrus.Debugf("no username found in SSH config or passed on command-line, assuming %s", username)
|
||||||
|
username = systemUser.Username
|
||||||
}
|
}
|
||||||
username := systemUser.Username
|
|
||||||
logrus.Debugf("no username found in SSH config, assuming %s", username)
|
|
||||||
sshUser = username
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if port = ssh_config.Get(hostname, "Port"); port == "" {
|
if port == "" {
|
||||||
logrus.Debugf("no port found in SSH config, assuming 22")
|
if port = ssh_config.Get(hostname, "Port"); port == "" {
|
||||||
port = "22"
|
logrus.Debugf("no port found in SSH config or passed on command-line, assuming 22")
|
||||||
|
port = "22"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyVal := "~/.ssh/identity"
|
dummyVal := "~/.ssh/identity"
|
||||||
@ -52,7 +55,7 @@ func GetHostConfig(hostname string, hasIdentityFile bool) (HostConfig, error) {
|
|||||||
hostConfig.Host = host
|
hostConfig.Host = host
|
||||||
hostConfig.IdentityFile = idf
|
hostConfig.IdentityFile = idf
|
||||||
hostConfig.Port = port
|
hostConfig.Port = port
|
||||||
hostConfig.User = sshUser
|
hostConfig.User = username
|
||||||
|
|
||||||
logrus.Debugf("constructed SSH config %s for %s", hostConfig, hostname)
|
logrus.Debugf("constructed SSH config %s for %s", hostConfig, hostname)
|
||||||
|
|
||||||
@ -60,7 +63,7 @@ func GetHostConfig(hostname string, hasIdentityFile bool) (HostConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SSH client connection.
|
// New creates a new SSH client connection.
|
||||||
func New(domainName, sshAuth string) (*simplessh.Client, error) {
|
func New(domainName, sshAuth, username, port string) (*simplessh.Client, error) {
|
||||||
var client *simplessh.Client
|
var client *simplessh.Client
|
||||||
|
|
||||||
hasIdentityFile := true
|
hasIdentityFile := true
|
||||||
@ -68,7 +71,7 @@ func New(domainName, sshAuth string) (*simplessh.Client, error) {
|
|||||||
hasIdentityFile = false
|
hasIdentityFile = false
|
||||||
}
|
}
|
||||||
|
|
||||||
hostConfig, err := GetHostConfig(domainName, hasIdentityFile)
|
hostConfig, err := GetHostConfig(domainName, username, port, hasIdentityFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return client, err
|
return client, err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user