From 8772217f4114c4e96c0b75764b4adf9a09740704 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Mon, 25 Oct 2021 10:06:16 +0200 Subject: [PATCH] fix: working provisioning post chaos testing --- cli/server/add.go | 122 +++++++++++++++++----------------------------- cli/server/new.go | 18 ++++++- pkg/ssh/ssh.go | 68 +++++++++++++++++++++++--- 3 files changed, 124 insertions(+), 84 deletions(-) diff --git a/cli/server/add.go b/cli/server/add.go index b37117b7..a1840f76 100644 --- a/cli/server/add.go +++ b/cli/server/add.go @@ -19,7 +19,6 @@ import ( "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" ) @@ -155,9 +154,23 @@ func newClient(c *cli.Context, domainName string) (*dockerClient.Client, error) } func installDocker(c *cli.Context, cl *dockerClient.Client, domainName string) error { - if _, err := cl.Info(c.Context); err != nil { - if strings.Contains(err.Error(), "command not found") { - fmt.Println(fmt.Sprintf(` + 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") + 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. @@ -174,81 +187,43 @@ source for this script can be seen here: `, domainName)) - response := false - prompt := &survey.Confirm{ - Message: fmt.Sprintf("attempt install docker on %s?", domainName), + response := false + prompt := &survey.Confirm{ + Message: fmt.Sprintf("attempt install docker on %s?", domainName), + } + if err := survey.AskOne(prompt, &response); err != nil { + return err + } + if !response { + logrus.Fatal("exiting as requested") + } + + cmd := "curl -s https://get.docker.com | bash" + + var sudoPass string + if askSudoPass { + prompt := &survey.Password{ + Message: "sudo password?", } - if err := survey.AskOne(prompt, &response); err != nil { + if err := survey.AskOne(prompt, &sudoPass); err != nil { return err } - if !response { - logrus.Fatal("exiting as requested") - } - - hasIdentityFile := true - if sshAuth == "password" { - hasIdentityFile = false - } - - hostConfig, err := ssh.GetHostConfig(domainName, hasIdentityFile) + logrus.Debugf("running '%s' on %s now with sudo password", cmd, domainName) + _, err := client.ExecSudo(cmd, sudoPass) if err != nil { return err } - - var client *simplessh.Client - if sshAuth == "identity-file" { - var err error - client, err = simplessh.ConnectWithAgent(hostConfig.Host, hostConfig.User) - if err != nil { - return err - } - } else { - password := "" - prompt := &survey.Password{ - Message: "SSH password?", - } - if err := survey.AskOne(prompt, &password); err != nil { - return err - } - - var err error - client, err = simplessh.ConnectWithPassword(hostConfig.Host, hostConfig.User, password) - if err != nil { - return err - } + } else { + logrus.Debugf("running '%s' on %s now without sudo password", cmd, domainName) + _, err := client.Exec(cmd) + if err != nil { + return err } - - defer client.Close() - logrus.Debugf("successfully created SSH client for %s", domainName) - - cmd := "curl -s https://get.docker.com | bash" - - var sudoPass string - if askSudoPass { - prompt := &survey.Password{ - Message: "sudo password?", - } - if err := survey.AskOne(prompt, &sudoPass); err != nil { - return err - } - logrus.Debugf("running '%s' on %s now with sudo password", cmd, domainName) - _, err := client.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) - if err != nil { - return err - } - } - - logrus.Infof("docker has successfully been installed on %s", domainName) - - return nil } } + + logrus.Infof("docker is installed on %s", domainName) + return nil } @@ -381,12 +356,7 @@ You may omit flags to avoid performing this provisioning logic. }, ArgsUsage: " [] []", Action: func(c *cli.Context) error { - if c.Args().Len() == 2 && !local { - err := errors.New("missing arguments or '--local'") - internal.ShowSubcommandHelpAndError(c, err) - } - - if c.Args().Get(2) != "" && local { + if c.Args().Len() > 0 && local { err := errors.New("cannot use '' and '--local' together") internal.ShowSubcommandHelpAndError(c, err) } diff --git a/cli/server/new.go b/cli/server/new.go index 9c86ea6e..924b5516 100644 --- a/cli/server/new.go +++ b/cli/server/new.go @@ -84,6 +84,18 @@ Your new Hetzner Cloud VPS has successfully been created! Here are the details: VPS IP address: %s VPS Root Password: %s +Here is what your SSH configuration (~/.ssh/config) might look like: + + Host %s.example.com + Hostname %s + User root + Port 22 + IdentityFile ~/.ssh/ + +Remember, your "Host" value must be a valid publicly accessible domain name as +Abra uses this domain to identity servers. If you specified "--ssh-auth +password" then you may skip the IdentityFile stanza. + You can access this new VPS via SSH using the following command: ssh root@%s @@ -92,7 +104,11 @@ Please note, this server is not managed by Abra yet (i.e. "abra server ls" will not list this server)! You will need to assign a domain name record ("abra record new") and add the server to your Abra configuration ("abra server add") to have a working server that you can deploy Co-op Cloud apps to. - `, internal.HetznerCloudName, ip, rootPassword, ip)) + `, + internal.HetznerCloudName, ip, rootPassword, + internal.HetznerCloudName, ip, + ip, + )) return nil } diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 1611cc92..dce734f3 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -2,8 +2,12 @@ package ssh import ( "fmt" + "os/user" + "github.com/AlecAivazis/survey/v2" "github.com/kevinburke/ssh_config" + "github.com/sfreiberg/simplessh" + "github.com/sirupsen/logrus" ) // HostConfig is a SSH host config. @@ -18,28 +22,78 @@ type HostConfig struct { func GetHostConfig(hostname string, hasIdentityFile bool) (HostConfig, error) { var hostConfig HostConfig - var host, user, port, idf string + var host, sshUser, port, idf string if host = ssh_config.Get(hostname, "Hostname"); host == "" { - return hostConfig, fmt.Errorf("SSH hostname missing for %s from SSH config", hostname) + logrus.Debugf("no hostname found in SSH config, assuming %s", hostname) + host = hostname } - if user = ssh_config.Get(hostname, "User"); user == "" { - return hostConfig, fmt.Errorf("SSH user missing for %s from SSH config", hostname) + if sshUser = ssh_config.Get(hostname, "User"); sshUser == "" { + 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 } if port = ssh_config.Get(hostname, "Port"); port == "" { - return hostConfig, fmt.Errorf("SSH port missing for %s from SSH config", hostname) + logrus.Debugf("no port found in SSH config, assuming 22") + port = "22" } - if idf = ssh_config.Get(hostname, "IdentityFile"); idf == "" && hasIdentityFile { + dummyVal := "~/.ssh/identity" + if idf = ssh_config.Get(hostname, "IdentityFile"); (idf == dummyVal || idf == "") && hasIdentityFile { return hostConfig, fmt.Errorf("SSH identity file missing for %s from SSH config", hostname) } hostConfig.Host = host hostConfig.IdentityFile = idf hostConfig.Port = port - hostConfig.User = user + hostConfig.User = sshUser + + logrus.Debugf("constructed SSH config %s for %s", hostConfig, hostname) return hostConfig, nil } + +// New creates a new SSH client connection. +func New(domainName, sshAuth string) (*simplessh.Client, error) { + var client *simplessh.Client + + hasIdentityFile := true + if sshAuth == "password" { + hasIdentityFile = false + } + + hostConfig, err := GetHostConfig(domainName, hasIdentityFile) + if err != nil { + return client, err + } + + if sshAuth == "identity-file" { + var err error + client, err = simplessh.ConnectWithAgent(hostConfig.Host, hostConfig.User) + if err != nil { + return client, err + } + } else { + password := "" + prompt := &survey.Password{ + Message: "SSH password?", + } + if err := survey.AskOne(prompt, &password); err != nil { + return client, err + } + + var err error + client, err = simplessh.ConnectWithPassword(hostConfig.Host, hostConfig.User, password) + if err != nil { + return client, err + } + } + + return client, nil +}