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 }
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 }