Пример #1
0
func (i *Instance) Run(command string, s *Streams) error {
	if s == nil {
		s = &Streams{}
	}
	var sc *ssh.Client
	err := sshAttempts.Run(func() (err error) {
		if s.Stderr != nil {
			fmt.Fprintf(s.Stderr, "Attempting to ssh to %s:22...\n", i.IP)
		}
		sc, err = i.DialSSH()
		return
	})
	if err != nil {
		return err
	}
	defer sc.Close()
	sess, err := sc.NewSession()
	sess.Stdin = s.Stdin
	sess.Stdout = s.Stdout
	sess.Stderr = s.Stderr
	if err := sess.Run(command); err != nil {
		return fmt.Errorf("failed to run command on %s: %s", i.IP, err)
	}
	return nil
}
Пример #2
0
func (c *BaseCluster) bootstrap() error {
	c.SendLog("Running bootstrap")

	if c.SSHKey == nil {
		return errors.New("No SSHKey found")
	}

	// bootstrap only needs to run on one instance
	ipAddress := c.InstanceIPs[0]

	sshConfig, err := c.sshConfig()
	if err != nil {
		return nil
	}

	attempts := 0
	maxAttempts := 3
	var sshConn *ssh.Client
	for {
		sshConn, err = ssh.Dial("tcp", ipAddress+":22", sshConfig)
		if err != nil {
			if attempts < maxAttempts {
				attempts += 1
				time.Sleep(time.Second)
				continue
			}
			return err
		}
		break
	}
	defer sshConn.Close()

	sess, err := sshConn.NewSession()
	if err != nil {
		return err
	}
	stdout, err := sess.StdoutPipe()
	if err != nil {
		return err
	}
	sess.Stderr = os.Stderr
	if err := sess.Start(fmt.Sprintf("CLUSTER_DOMAIN=%s flynn-host bootstrap --json", c.Domain.Name)); err != nil {
		c.uploadDebugInfo(sshConfig, ipAddress)
		return err
	}

	var keyData struct {
		Key string `json:"data"`
	}
	var loginTokenData struct {
		Token string `json:"data"`
	}
	var controllerCertData struct {
		Pin    string `json:"pin"`
		CACert string `json:"ca_cert"`
	}
	output := json.NewDecoder(stdout)
	for {
		var stepRaw json.RawMessage
		if err := output.Decode(&stepRaw); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		var step stepInfo
		if err := json.Unmarshal(stepRaw, &step); err != nil {
			return err
		}
		if step.State == "error" {
			c.uploadDebugInfo(sshConfig, ipAddress)
			return fmt.Errorf("bootstrap: %s %s error: %s", step.ID, step.Action, step.Error)
		}
		c.SendLog(fmt.Sprintf("%s: %s", step.ID, step.State))
		if step.State != "done" {
			continue
		}
		switch step.ID {
		case "controller-key":
			if err := json.Unmarshal(*step.Data, &keyData); err != nil {
				return err
			}
		case "controller-cert":
			if err := json.Unmarshal(*step.Data, &controllerCertData); err != nil {
				return err
			}
		case "dashboard-login-token":
			if err := json.Unmarshal(*step.Data, &loginTokenData); err != nil {
				return err
			}
		case "log-complete":
			break
		}
	}
	if keyData.Key == "" || controllerCertData.Pin == "" {
		return err
	}

	c.ControllerKey = keyData.Key
	c.ControllerPin = controllerCertData.Pin
	c.CACert = controllerCertData.CACert
	c.DashboardLoginToken = loginTokenData.Token

	if err := c.saveField("ControllerKey", c.ControllerKey); err != nil {
		return err
	}
	if err := c.saveField("ControllerPin", c.ControllerPin); err != nil {
		return err
	}
	if err := c.saveField("CACert", c.CACert); err != nil {
		return err
	}
	if err := c.saveField("DashboardLoginToken", c.DashboardLoginToken); err != nil {
		return err
	}

	if err := sess.Wait(); err != nil {
		return err
	}
	return nil
}
Пример #3
0
func (c *BaseCluster) instanceRunCmdWithClient(cmd string, sshConn *ssh.Client, user, ipAddress string) error {
	c.SendLog(fmt.Sprintf("Running `%s` on %s", cmd, ipAddress))
	sudoPrompt := "<SUDO_PROMPT>"
	cmd = strings.Replace(cmd, "sudo ", fmt.Sprintf("sudo -S --prompt='%s\n' ", sudoPrompt), -1)

	sess, err := sshConn.NewSession()
	if err != nil {
		return err
	}
	stdin, err := sess.StdinPipe()
	if err != nil {
		return err
	}
	stdout, err := sess.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := sess.StderrPipe()
	if err != nil {
		return err
	}
	if err = sess.Start(cmd); err != nil {
		return err
	}

	doneChan := make(chan struct{})
	defer close(doneChan)

	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			select {
			case _, _ = <-doneChan:
				return
			default:
			}

			c.SendLog(scanner.Text())
		}
	}()
	go func() {
		passwordPrompt := func(msg string, useCache bool) {
			c.passwordPromptMtx.Lock()
			var password string
			var ok bool
			if useCache {
				password, ok = c.passwordCache[ipAddress]
			}
			if !ok || password == "" {
				password = c.PromptProtectedInput(msg)
				c.passwordCache[ipAddress] = password
			} else {
				c.SendLog("Using cached password")
			}
			if _, err := fmt.Fprintf(stdin, "%s\n", password); err != nil {
				c.SendLog(err.Error())
			}
			c.passwordPromptMtx.Unlock()
		}

		scanner := bufio.NewScanner(stderr)
		var prevLine string
		for scanner.Scan() {
			select {
			case _, _ = <-doneChan:
				return
			default:
			}

			line := scanner.Text()
			c.SendLog(line)

			msg := fmt.Sprintf("Please enter your sudo password for %s@%s", user, ipAddress)
			if prevLine == sudoPrompt && line == "Sorry, try again." {
				passwordPrompt(fmt.Sprintf("%s\n%s", line, msg), false)
			} else if line == sudoPrompt {
				passwordPrompt(msg, true)
			}
			prevLine = line
		}
	}()

	return sess.Wait()
}
Пример #4
0
func (c *BaseCluster) instanceRunCmdWithClient(cmd string, sshConn *ssh.Client, user, ipAddress string) error {
	c.SendLog(fmt.Sprintf("Running `%s` on %s", cmd, ipAddress))
	sudoPrompt := "<SUDO_PROMPT>"
	cmd = strings.Replace(cmd, "sudo ", fmt.Sprintf("sudo -S --prompt='%s\n' ", sudoPrompt), -1)

	sess, err := sshConn.NewSession()
	if err != nil {
		return err
	}
	stdin, err := sess.StdinPipe()
	if err != nil {
		return err
	}
	stdout, err := sess.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := sess.StderrPipe()
	if err != nil {
		return err
	}
	if err = sess.Start(cmd); err != nil {
		return err
	}

	doneChan := make(chan struct{})
	defer close(doneChan)

	go func() {
		scanner := bufio.NewScanner(stdout)
		var prevLine string
		for scanner.Scan() {
			select {
			case _, _ = <-doneChan:
				return
			default:
			}

			line := scanner.Text()
			c.SendLog(line)

			// Handle prompt to remove Flynn when installing using --clean
			flynnRemovePromptMsgs := []string{
				"About to stop Flynn and remove all existing data",
				"Are you sure this is what you want?",
			}
			if strings.Contains(prevLine, flynnRemovePromptMsgs[0]) && strings.Contains(line, flynnRemovePromptMsgs[1]) {
				answer := "no"
				if c.YesNoPrompt("If Flynn is already installed, it will be stopped and removed along with all data before installing the latest version. Would you like to proceed?") {
					answer = "yes"
				} else {
					c.Abort()
				}
				if _, err := fmt.Fprintf(stdin, "%s\n", answer); err != nil {
					c.SendLog(err.Error())
				}
			}
			prevLine = line
		}
	}()
	go func() {
		passwordPrompt := func(msg string, useCache bool) {
			c.passwordPromptMtx.Lock()
			var password string
			var ok bool
			if useCache {
				password, ok = c.passwordCache[ipAddress]
			}
			if !ok || password == "" {
				password = c.PromptProtectedInput(msg)
				c.passwordCache[ipAddress] = password
			} else {
				c.SendLog("Using cached password")
			}
			if _, err := fmt.Fprintf(stdin, "%s\n", password); err != nil {
				c.SendLog(err.Error())
			}
			c.passwordPromptMtx.Unlock()
		}

		scanner := bufio.NewScanner(stderr)
		var prevLine string
		for scanner.Scan() {
			select {
			case _, _ = <-doneChan:
				return
			default:
			}

			line := scanner.Text()
			c.SendLog(line)

			msg := fmt.Sprintf("Please enter your sudo password for %s@%s", user, ipAddress)
			if prevLine == sudoPrompt && line == "Sorry, try again." {
				passwordPrompt(fmt.Sprintf("%s\n%s", line, msg), false)
			} else if line == sudoPrompt {
				passwordPrompt(msg, true)
			}
			prevLine = line
		}
	}()

	return sess.Wait()
}