forked from coop-cloud/abra
fix: grand ssh, provisioning, perms refactor
See coop-cloud/organising#280. See coop-cloud/organising#273.
This commit is contained in:
parent
4c6d52c426
commit
a07e71f7df
|
@ -57,11 +57,11 @@ Supported shells are as follows:
|
||||||
}
|
}
|
||||||
|
|
||||||
autocompletionDir := path.Join(config.ABRA_DIR, "autocompletion")
|
autocompletionDir := path.Join(config.ABRA_DIR, "autocompletion")
|
||||||
if err := os.Mkdir(autocompletionDir, 0644); err != nil {
|
if err := os.Mkdir(autocompletionDir, 0764); err != nil {
|
||||||
if !os.IsExist(err) {
|
if !os.IsExist(err) {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
logrus.Debugf("'%s' already created, moving on...", autocompletionDir)
|
logrus.Debugf("%s already created", autocompletionDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
autocompletionFile := path.Join(config.ABRA_DIR, "autocompletion", shellType)
|
autocompletionFile := path.Join(config.ABRA_DIR, "autocompletion", shellType)
|
||||||
|
|
|
@ -201,7 +201,7 @@ A new catalogue copy can be published to the recipes repository by passing the
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(config.APPS_JSON); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(config.APPS_JSON); err != nil && os.IsNotExist(err) {
|
||||||
if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0644); err != nil {
|
if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0764); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,7 +216,7 @@ A new catalogue copy can be published to the recipes repository by passing the
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(config.APPS_JSON, updatedRecipesJSON, 0644); err != nil {
|
if err := ioutil.WriteFile(config.APPS_JSON, updatedRecipesJSON, 0764); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ convenient command-line experience. See "abra autocomplete -h" for more.
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
if err := os.Mkdir(path, 0644); err != nil {
|
if err := os.Mkdir(path, 0764); err != nil {
|
||||||
if !os.IsExist(err) {
|
if !os.IsExist(err) {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,11 +129,11 @@ func NewAction(c *cli.Context) error {
|
||||||
|
|
||||||
sanitisedAppName := config.SanitiseAppName(NewAppName)
|
sanitisedAppName := config.SanitiseAppName(NewAppName)
|
||||||
if len(sanitisedAppName) > 45 {
|
if len(sanitisedAppName) > 45 {
|
||||||
logrus.Fatalf("'%s' cannot be longer than 45 characters", sanitisedAppName)
|
logrus.Fatalf("%s cannot be longer than 45 characters", sanitisedAppName)
|
||||||
}
|
}
|
||||||
logrus.Debugf("'%s' sanitised as '%s' for new app", NewAppName, sanitisedAppName)
|
logrus.Debugf("%s sanitised as %s for new app", NewAppName, sanitisedAppName)
|
||||||
|
|
||||||
if err := config.TemplateAppEnvSample(recipe.Name, NewAppName, NewAppServer, Domain, recipe.Name); err != nil {
|
if err := config.TemplateAppEnvSample(recipe.Name, NewAppName, NewAppServer, Domain); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ The new example repository is cloned to ~/.abra/apps/<recipe>.
|
||||||
path.Join(config.APPS_DIR, recipeName, ".drone.yml"),
|
path.Join(config.APPS_DIR, recipeName, ".drone.yml"),
|
||||||
}
|
}
|
||||||
for _, path := range toParse {
|
for _, path := range toParse {
|
||||||
file, err := os.OpenFile(path, os.O_RDWR, 0644)
|
file, err := os.OpenFile(path, os.O_RDWR, 0664)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
abraFormatter "coopcloud.tech/abra/cli/formatter"
|
|
||||||
"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/config"
|
||||||
|
@ -127,7 +126,7 @@ func installDockerLocal(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("bash", "-c", "wget -O- https://install.abra.coopcloud.tech | bash")
|
cmd := exec.Command("bash", "-c", "wget -O- https://get.docker.com | bash")
|
||||||
if err := internal.RunCmd(cmd); err != nil {
|
if err := internal.RunCmd(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -219,14 +218,21 @@ func installDocker(c *cli.Context, cl *dockerClient.Client, sshCl *ssh.Client, d
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
Message: fmt.Sprintf("attempt install docker on %s?", domainName),
|
Message: fmt.Sprintf("attempt install docker on %s?", domainName),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
if err := survey.AskOne(prompt, &response); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !response {
|
if !response {
|
||||||
logrus.Fatal("exiting as requested")
|
logrus.Fatal("exiting as requested")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, exe := range []string{"wget", "bash"} {
|
exes := []string{"wget", "bash"}
|
||||||
|
if askSudoPass {
|
||||||
|
exes = append(exes, "ssh-askpass")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, exe := range exes {
|
||||||
exists, err := ensureRemoteExecutable(exe, sshCl)
|
exists, err := ensureRemoteExecutable(exe, sshCl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -236,29 +242,78 @@ func installDocker(c *cli.Context, cl *dockerClient.Client, sshCl *ssh.Client, d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := "wget -O- https://install.abra.coopcloud.tech | bash"
|
|
||||||
|
|
||||||
var sudoPass string
|
var sudoPass string
|
||||||
if askSudoPass {
|
if askSudoPass {
|
||||||
|
cmd := "wget -O- https://get.docker.com | bash"
|
||||||
|
|
||||||
prompt := &survey.Password{
|
prompt := &survey.Password{
|
||||||
Message: "sudo password?",
|
Message: "sudo password?",
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &sudoPass); err != nil {
|
if err := survey.AskOne(prompt, &sudoPass); err != nil {
|
||||||
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)
|
||||||
|
|
||||||
|
if sudoPass == "" {
|
||||||
|
return fmt.Errorf("missing sudo password but requested --ask-sudo-pass?")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Warn("installing docker, this could take some time...")
|
||||||
|
|
||||||
if err := ssh.RunSudoCmd(cmd, sudoPass, sshCl); err != nil {
|
if err := ssh.RunSudoCmd(cmd, sudoPass, sshCl); err != nil {
|
||||||
|
fmt.Print(fmt.Sprintf(`
|
||||||
|
Abra was unable to bootstrap Docker, see below for logs:
|
||||||
|
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
If nothing works, you try running the Docker install script manually on your server:
|
||||||
|
|
||||||
|
wget -O- https://get.docker.com | bash
|
||||||
|
|
||||||
|
`, string(err.Error())))
|
||||||
|
logrus.Fatal("Process exited with status 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("docker is installed on %s", domainName)
|
||||||
|
|
||||||
|
remoteUser := sshCl.SSHClient.Conn.User()
|
||||||
|
logrus.Infof("adding %s to docker group", remoteUser)
|
||||||
|
permsCmd := fmt.Sprintf("sudo usermod -aG docker %s", remoteUser)
|
||||||
|
if err := ssh.RunSudoCmd(permsCmd, sudoPass, sshCl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
cmd := "wget -O- https://get.docker.com | bash"
|
||||||
|
|
||||||
logrus.Debugf("running %s on %s now without sudo password", cmd, domainName)
|
logrus.Debugf("running %s on %s now without sudo password", cmd, domainName)
|
||||||
if err := ssh.Exec(cmd, sshCl); err != nil {
|
|
||||||
return err
|
logrus.Warn("installing docker, this could take some time...")
|
||||||
}
|
|
||||||
}
|
if out, err := sshCl.Exec(cmd); err != nil {
|
||||||
|
fmt.Print(fmt.Sprintf(`
|
||||||
|
Abra was unable to bootstrap Docker, see below for logs:
|
||||||
|
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
This could be due to a number of things but one of the most common is that your
|
||||||
|
server user account does not have sudo access, and if it does, you need to pass
|
||||||
|
"--ask-sudo-pass" in order to supply Abra with your password.
|
||||||
|
|
||||||
|
If nothing works, you try running the Docker install script manually on your server:
|
||||||
|
|
||||||
|
wget -O- https://get.docker.com | bash
|
||||||
|
|
||||||
|
`, string(out)))
|
||||||
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("docker is already installed on %s", domainName)
|
logrus.Infof("docker is installed on %s", domainName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -266,10 +321,12 @@ func installDocker(c *cli.Context, cl *dockerClient.Client, sshCl *ssh.Client, d
|
||||||
func initSwarmLocal(c *cli.Context, cl *dockerClient.Client, domainName string) error {
|
func initSwarmLocal(c *cli.Context, cl *dockerClient.Client, domainName string) error {
|
||||||
initReq := swarm.InitRequest{ListenAddr: "0.0.0.0:2377"}
|
initReq := swarm.InitRequest{ListenAddr: "0.0.0.0:2377"}
|
||||||
if _, err := cl.SwarmInit(c.Context, initReq); err != nil {
|
if _, err := cl.SwarmInit(c.Context, initReq); err != nil {
|
||||||
if !strings.Contains(err.Error(), "is already part of a swarm") {
|
if strings.Contains(err.Error(), "is already part of a swarm") ||
|
||||||
|
strings.Contains(err.Error(), "must specify a listening address") {
|
||||||
|
logrus.Infof("swarm mode already initialised on %s", domainName)
|
||||||
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Info("swarm mode already initialised on local server")
|
|
||||||
} else {
|
} else {
|
||||||
logrus.Infof("initialised swarm mode on local server")
|
logrus.Infof("initialised swarm mode on local server")
|
||||||
}
|
}
|
||||||
|
@ -298,11 +355,12 @@ func initSwarm(c *cli.Context, cl *dockerClient.Client, domainName string) error
|
||||||
AdvertiseAddr: ipv4,
|
AdvertiseAddr: ipv4,
|
||||||
}
|
}
|
||||||
if _, err := cl.SwarmInit(c.Context, initReq); err != nil {
|
if _, err := cl.SwarmInit(c.Context, initReq); err != nil {
|
||||||
if !strings.Contains(err.Error(), "is already part of a swarm") ||
|
if strings.Contains(err.Error(), "is already part of a swarm") ||
|
||||||
!strings.Contains(err.Error(), "must specify a listening address") {
|
strings.Contains(err.Error(), "must specify a listening address") {
|
||||||
|
logrus.Infof("swarm mode already initialised on %s", domainName)
|
||||||
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Infof("swarm mode already initialised on %s", domainName)
|
|
||||||
} else {
|
} else {
|
||||||
logrus.Infof("initialised swarm mode on %s", domainName)
|
logrus.Infof("initialised swarm mode on %s", domainName)
|
||||||
}
|
}
|
||||||
|
@ -339,16 +397,8 @@ func deployTraefik(c *cli.Context, cl *dockerClient.Client, domainName string) e
|
||||||
internal.NewAppName = fmt.Sprintf("%s_%s", "traefik", config.SanitiseAppName(domainName))
|
internal.NewAppName = fmt.Sprintf("%s_%s", "traefik", config.SanitiseAppName(domainName))
|
||||||
|
|
||||||
appEnvPath := path.Join(config.ABRA_DIR, "servers", internal.Domain, fmt.Sprintf("%s.env", internal.NewAppName))
|
appEnvPath := path.Join(config.ABRA_DIR, "servers", internal.Domain, fmt.Sprintf("%s.env", internal.NewAppName))
|
||||||
if _, err := os.Stat(appEnvPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(appEnvPath); os.IsNotExist(err) {
|
||||||
fmt.Println(fmt.Sprintf(`
|
logrus.Info(fmt.Sprintf("-t/--traefik specified, automatically deploying traefik to %s", internal.NewAppServer))
|
||||||
You specified "--traefik/-t" and that means that Abra will now try to
|
|
||||||
automatically create a new Traefik app on %s.
|
|
||||||
`, internal.NewAppServer))
|
|
||||||
|
|
||||||
tableCol := []string{"recipe", "domain", "server", "name"}
|
|
||||||
table := abraFormatter.CreateTable(tableCol)
|
|
||||||
table.Append([]string{internal.RecipeName, internal.Domain, internal.NewAppServer, internal.NewAppName})
|
|
||||||
|
|
||||||
if err := internal.NewAction(c); err != nil {
|
if err := internal.NewAction(c); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -515,7 +565,7 @@ func ensureLocalExecutable(exe string) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(out) == "", nil
|
return string(out) != "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureRemoteExecutable ensures that an executable is present on a remote machine
|
// ensureRemoteExecutable ensures that an executable is present on a remote machine
|
||||||
|
@ -525,5 +575,5 @@ func ensureRemoteExecutable(exe string, sshCl *ssh.Client) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(out) == "", nil
|
return string(out) != "", nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ like tears in rain.
|
||||||
|
|
||||||
response := false
|
response := false
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
Message: "are you sure there is no server to delete?",
|
Message: "prompt to actual server deletion?",
|
||||||
}
|
}
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
if err := survey.AskOne(prompt, &response); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
|
|
|
@ -209,7 +209,7 @@ func readRecipeCatalogueWeb(target interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0644); err != nil {
|
if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0764); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ func UpdateTag(pattern, image, tag, recipeName string) error {
|
||||||
|
|
||||||
logrus.Debugf("updating '%s' to '%s' in '%s'", old, new, compose.Filename)
|
logrus.Debugf("updating '%s' to '%s' in '%s'", old, new, compose.Filename)
|
||||||
|
|
||||||
if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0644); err != nil {
|
if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func UpdateLabel(pattern, serviceName, label, recipeName string) error {
|
||||||
|
|
||||||
logrus.Debugf("updating %s to %s in %s", old, label, compose.Filename)
|
logrus.Debugf("updating %s to %s in %s", old, label, compose.Filename)
|
||||||
|
|
||||||
if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0644); err != nil {
|
if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -248,22 +248,22 @@ func GetAppNames() ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateAppEnvSample copies the example env file for the app into the users env files
|
// TemplateAppEnvSample copies the example env file for the app into the users env files
|
||||||
func TemplateAppEnvSample(appType, appName, server, domain, recipe string) error {
|
func TemplateAppEnvSample(recipe, appName, server, domain string) error {
|
||||||
envSamplePath := path.Join(ABRA_DIR, "apps", appType, ".env.sample")
|
envSamplePath := path.Join(ABRA_DIR, "apps", recipe, ".env.sample")
|
||||||
envSample, err := ioutil.ReadFile(envSamplePath)
|
envSample, err := ioutil.ReadFile(envSamplePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
appEnvPath := path.Join(ABRA_DIR, "servers", server, fmt.Sprintf("%s.env", appName))
|
appEnvPath := path.Join(ABRA_DIR, "servers", server, fmt.Sprintf("%s.env", appName))
|
||||||
if _, err := os.Stat(appEnvPath); err == nil {
|
if _, err := os.Stat(appEnvPath); os.IsExist(err) {
|
||||||
return fmt.Errorf("%s already exists?", appEnvPath)
|
return fmt.Errorf("%s already exists?", appEnvPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
envSample = []byte(strings.Replace(string(envSample), fmt.Sprintf("%s.example.com", recipe), domain, -1))
|
envSample = []byte(strings.Replace(string(envSample), fmt.Sprintf("%s.example.com", recipe), domain, -1))
|
||||||
envSample = []byte(strings.Replace(string(envSample), "example.com", domain, -1))
|
envSample = []byte(strings.Replace(string(envSample), "example.com", domain, -1))
|
||||||
|
|
||||||
err = ioutil.WriteFile(appEnvPath, envSample, 0644)
|
err = ioutil.WriteFile(appEnvPath, envSample, 0664)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ func GetAllFoldersInDirectory(directory string) ([]string, error) {
|
||||||
func EnsureAbraDirExists() error {
|
func EnsureAbraDirExists() error {
|
||||||
if _, err := os.Stat(ABRA_DIR); os.IsNotExist(err) {
|
if _, err := os.Stat(ABRA_DIR); os.IsNotExist(err) {
|
||||||
logrus.Debugf("%s does not exist, creating it", ABRA_DIR)
|
logrus.Debugf("%s does not exist, creating it", ABRA_DIR)
|
||||||
if err := os.Mkdir(ABRA_DIR, 0777); err != nil {
|
if err := os.Mkdir(ABRA_DIR, 0764); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
func CreateServerDir(serverName string) error {
|
func CreateServerDir(serverName string) error {
|
||||||
serverPath := path.Join(config.ABRA_DIR, "servers", serverName)
|
serverPath := path.Join(config.ABRA_DIR, "servers", serverName)
|
||||||
|
|
||||||
if err := os.Mkdir(serverPath, 0644); err != nil {
|
if err := os.Mkdir(serverPath, 0764); err != nil {
|
||||||
if !os.IsExist(err) {
|
if !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ type sudoWriter struct {
|
||||||
|
|
||||||
// Write satisfies the write interface for sudoWriter
|
// Write satisfies the write interface for sudoWriter
|
||||||
func (w *sudoWriter) Write(p []byte) (int, error) {
|
func (w *sudoWriter) Write(p []byte) (int, error) {
|
||||||
if string(p) == "sudo_password" {
|
if strings.Contains(string(p), "sudo_password") {
|
||||||
w.stdin.Write([]byte(w.pw + "\n"))
|
w.stdin.Write([]byte(w.pw + "\n"))
|
||||||
w.pw = ""
|
w.pw = ""
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
|
@ -131,11 +131,9 @@ func RunSudoCmd(cmd, passwd string, cl *Client) error {
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
|
|
||||||
cmd = "sudo -p " + "sudo_password" + " -S " + cmd
|
sudoCmd := fmt.Sprintf("SSH_ASKPASS=/usr/bin/ssh-askpass; sudo -p sudo_password -S %s", cmd)
|
||||||
|
|
||||||
w := &sudoWriter{
|
w := &sudoWriter{pw: passwd}
|
||||||
pw: passwd,
|
|
||||||
}
|
|
||||||
w.stdin, err = session.StdinPipe()
|
w.stdin, err = session.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -144,79 +142,19 @@ func RunSudoCmd(cmd, passwd string, cl *Client) error {
|
||||||
session.Stdout = w
|
session.Stdout = w
|
||||||
session.Stderr = w
|
session.Stderr = w
|
||||||
|
|
||||||
done := make(chan struct{})
|
modes := ssh.TerminalModes{
|
||||||
scanner := bufio.NewScanner(session.Stdin)
|
ssh.ECHO: 0,
|
||||||
|
ssh.TTY_OP_ISPEED: 14400,
|
||||||
go func() {
|
ssh.TTY_OP_OSPEED: 14400,
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := session.Start(cmd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<-done
|
err = session.RequestPty("xterm", 80, 40, modes)
|
||||||
|
|
||||||
if err := session.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exec runs a command on a remote and streams output
|
|
||||||
func Exec(cmd string, cl *Client) error {
|
|
||||||
session, err := cl.SSHClient.NewSession()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
stdout, err := session.StdoutPipe()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr, err := session.StdoutPipe()
|
if err := session.Run(sudoCmd); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("%s", string(w.b.Bytes()))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stdoutDone := make(chan struct{})
|
|
||||||
stdoutScanner := bufio.NewScanner(stdout)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for stdoutScanner.Scan() {
|
|
||||||
line := stdoutScanner.Text()
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
stdoutDone <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
stderrDone := make(chan struct{})
|
|
||||||
stderrScanner := bufio.NewScanner(stderr)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for stderrScanner.Scan() {
|
|
||||||
line := stderrScanner.Text()
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
stderrDone <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := session.Start(cmd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
<-stdoutDone
|
|
||||||
<-stderrDone
|
|
||||||
|
|
||||||
if err := session.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -320,7 +258,7 @@ func HostKeyAddCallback(hostnameAndPort string, remote net.Addr, pubKey ssh.Publ
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
hostname := strings.Split(hostnameAndPort, ":")[0]
|
hostname := strings.Split(hostnameAndPort, ":")[0]
|
||||||
logrus.Debugf("server SSH host key found for %s, moving on", hostname)
|
logrus.Debugf("server SSH host key found for %s", hostname)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue