forked from toolshed/abra
		
	refactor: better SSH connection details handling
This commit is contained in:
		| @ -24,18 +24,16 @@ var recipeNewCommand = &cli.Command{ | ||||
| 		directory := path.Join(config.APPS_DIR, recipe.Name) | ||||
| 		if _, err := os.Stat(directory); !os.IsNotExist(err) { | ||||
| 			logrus.Fatalf("'%s' recipe directory already exists?", directory) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL) | ||||
| 		if err := git.Clone(directory, url); err != nil { | ||||
| 			return err | ||||
| 			logrus.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		gitRepo := path.Join(config.APPS_DIR, recipe.Name, ".git") | ||||
| 		if err := os.RemoveAll(gitRepo); err != nil { | ||||
| 			logrus.Fatal(err) | ||||
| 			return nil | ||||
| 		} | ||||
| 		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) | ||||
| 			if err != nil { | ||||
| 				logrus.Fatal(err) | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			tpl, err := template.ParseFiles(path) | ||||
| 			if err != nil { | ||||
| 				logrus.Fatal(err) | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			// TODO: ask for description and probably other things so that the | ||||
| @ -65,7 +61,6 @@ var recipeNewCommand = &cli.Command{ | ||||
| 				Description string | ||||
| 			}{recipe.Name, "TODO"}); err != nil { | ||||
| 				logrus.Fatal(err) | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -8,21 +8,43 @@ import ( | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"os/user" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"coopcloud.tech/abra/cli/internal" | ||||
| 	"coopcloud.tech/abra/pkg/client" | ||||
| 	"coopcloud.tech/abra/pkg/config" | ||||
| 	"coopcloud.tech/abra/pkg/server" | ||||
| 	"coopcloud.tech/abra/pkg/ssh" | ||||
| 	"github.com/AlecAivazis/survey/v2" | ||||
| 	"github.com/docker/docker/api/types" | ||||
| 	"github.com/docker/docker/api/types/swarm" | ||||
| 	dockerClient "github.com/docker/docker/client" | ||||
| 	"github.com/sfreiberg/simplessh" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"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 localFlag = &cli.BoolFlag{ | ||||
| 	Name:        "local", | ||||
| @ -68,6 +90,40 @@ var traefikFlag = &cli.BoolFlag{ | ||||
| 	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 { | ||||
| 	if err := createServerDir(domainName); err != nil { | ||||
| 		return err | ||||
| @ -80,7 +136,7 @@ func newLocalServer(c *cli.Context, domainName string) error { | ||||
|  | ||||
| 	if _, err := exec.LookPath("docker"); err != nil { | ||||
| 		if provision { | ||||
| 			if err := installDocker(c, cl, domainName); err != nil { | ||||
| 			if err := installDockerLocal(c); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if err := initSwarm(c, cl, domainName); err != nil { | ||||
| @ -104,21 +160,7 @@ func newLocalServer(c *cli.Context, domainName string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func newContext(c *cli.Context, domainName 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" | ||||
| 	} | ||||
|  | ||||
| func newContext(c *cli.Context, domainName, username, port string) error { | ||||
| 	store := client.NewDefaultDockerContextStore() | ||||
| 	contexts, err := store.Store.List() | ||||
| 	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) { | ||||
| 	cl, err := client.New(domainName) | ||||
| 	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 cl, nil | ||||
| } | ||||
|  | ||||
| func installDocker(c *cli.Context, cl *dockerClient.Client, domainName string) error { | ||||
| 	logrus.Debugf("attempting to construct SSH client for %s", domainName) | ||||
|  | ||||
| 	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") | ||||
| func installDocker(c *cli.Context, cl *dockerClient.Client, sshCl *simplessh.Client, domainName string) error { | ||||
| 	result, err := sshCl.Exec("which docker") | ||||
| 	if err != nil && string(result) != "" { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if string(result) == "" { | ||||
| 		fmt.Println(fmt.Sprintf(` | ||||
| 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)) | ||||
| 		fmt.Println(fmt.Sprintf(dockerInstallMsg, domainName)) | ||||
|  | ||||
| 		response := false | ||||
| 		prompt := &survey.Confirm{ | ||||
| @ -209,13 +222,13 @@ source for this script can be seen here: | ||||
| 				return err | ||||
| 			} | ||||
| 			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 { | ||||
| 				return err | ||||
| 			} | ||||
| 		} else { | ||||
| 			logrus.Debugf("running '%s' on %s now without sudo password", cmd, domainName) | ||||
| 			_, err := client.Exec(cmd) | ||||
| 			_, err := sshCl.Exec(cmd) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| @ -378,11 +391,25 @@ You may omit flags to avoid performing this provisioning logic. | ||||
| 			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 { | ||||
| 			logrus.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		if err := newContext(c, domainName); err != nil { | ||||
| 		if err := newContext(c, domainName, username, port); err != nil { | ||||
| 			logrus.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| @ -392,7 +419,15 @@ You may omit flags to avoid performing this provisioning logic. | ||||
| 		} | ||||
|  | ||||
| 		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) | ||||
| 			} | ||||
| 			if err := initSwarm(c, cl, domainName); err != nil { | ||||
|  | ||||
| @ -19,30 +19,33 @@ type HostConfig struct { | ||||
| } | ||||
|  | ||||
| // 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 host, sshUser, port, idf string | ||||
| 	var host, idf string | ||||
|  | ||||
| 	if host = ssh_config.Get(hostname, "Hostname"); host == "" { | ||||
| 		logrus.Debugf("no hostname found in SSH config, assuming %s", hostname) | ||||
| 		host = hostname | ||||
| 	} | ||||
|  | ||||
| 	if sshUser = ssh_config.Get(hostname, "User"); sshUser == "" { | ||||
| 	if username == "" { | ||||
| 		if username = ssh_config.Get(hostname, "User"); username == "" { | ||||
| 			systemUser, err := user.Current() | ||||
| 			if err != nil { | ||||
| 				return hostConfig, err | ||||
| 			} | ||||
| 		username := systemUser.Username | ||||
| 		logrus.Debugf("no username found in SSH config, assuming %s", username) | ||||
| 		sshUser = username | ||||
| 			logrus.Debugf("no username found in SSH config or passed on command-line, assuming %s", username) | ||||
| 			username = systemUser.Username | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if port == "" { | ||||
| 		if port = ssh_config.Get(hostname, "Port"); port == "" { | ||||
| 		logrus.Debugf("no port found in SSH config, assuming 22") | ||||
| 			logrus.Debugf("no port found in SSH config or passed on command-line, assuming 22") | ||||
| 			port = "22" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	dummyVal := "~/.ssh/identity" | ||||
| 	if idf = ssh_config.Get(hostname, "IdentityFile"); (idf == dummyVal || idf == "") && hasIdentityFile { | ||||
| @ -52,7 +55,7 @@ func GetHostConfig(hostname string, hasIdentityFile bool) (HostConfig, error) { | ||||
| 	hostConfig.Host = host | ||||
| 	hostConfig.IdentityFile = idf | ||||
| 	hostConfig.Port = port | ||||
| 	hostConfig.User = sshUser | ||||
| 	hostConfig.User = username | ||||
|  | ||||
| 	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. | ||||
| func New(domainName, sshAuth string) (*simplessh.Client, error) { | ||||
| func New(domainName, sshAuth, username, port string) (*simplessh.Client, error) { | ||||
| 	var client *simplessh.Client | ||||
|  | ||||
| 	hasIdentityFile := true | ||||
| @ -68,7 +71,7 @@ func New(domainName, sshAuth string) (*simplessh.Client, error) { | ||||
| 		hasIdentityFile = false | ||||
| 	} | ||||
|  | ||||
| 	hostConfig, err := GetHostConfig(domainName, hasIdentityFile) | ||||
| 	hostConfig, err := GetHostConfig(domainName, username, port, hasIdentityFile) | ||||
| 	if err != nil { | ||||
| 		return client, err | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user