// setupHost runs the specified setup script for an individual host. Returns // the output from running the script remotely, as well as any error that // occurs. If the script exits with a non-zero exit code, the error will be non-nil. func (init *HostInit) setupHost(targetHost *host.Host) (string, error) { // fetch the appropriate cloud provider for the host cloudMgr, err := providers.GetCloudManager(targetHost.Provider, init.Settings) if err != nil { return "", fmt.Errorf("failed to get cloud manager for host %v with provider %v: %v", targetHost.Id, targetHost.Provider, err) } // mark the host as initializing if err := targetHost.SetInitializing(); err != nil { if err == mgo.ErrNotFound { return "", ErrHostAlreadyInitializing } else { return "", fmt.Errorf("database error: %v", err) } } /* TESTING ONLY setupDebugSSHTunnel(path_to_ssh_key, targetHost.User, targetHost.Host) */ // run the function scheduled for when the host is up err = cloudMgr.OnUp(targetHost) if err != nil { // if this fails it is probably due to an API hiccup, so we keep going. evergreen.Logger.Logf(slogger.WARN, "OnUp callback failed for host '%v': '%v'", targetHost.Id, err) } cloudHost, err := providers.GetCloudHost(targetHost, init.Settings) if err != nil { return "", fmt.Errorf("failed to get cloud host for %v: %v", targetHost.Id, err) } sshOptions, err := cloudHost.GetSSHOptions() if err != nil { return "", fmt.Errorf("error getting ssh options for host %v: %v", targetHost.Id, err) } if targetHost.Distro.Teardown != "" { err = init.copyScript(targetHost, teardownScriptName, targetHost.Distro.Teardown) if err != nil { return "", fmt.Errorf("error copying script %v to host %v: %v", teardownScriptName, targetHost.Id, err) } } if targetHost.Distro.Setup != "" { err = init.copyScript(targetHost, setupScriptName, targetHost.Distro.Setup) if err != nil { return "", fmt.Errorf("error copying script %v to host %v: %v", setupScriptName, targetHost.Id, err) } logs, err := hostutil.RunRemoteScript(targetHost, setupScriptName, sshOptions) if err != nil { return logs, fmt.Errorf("error running setup script over ssh: %v", err) } return logs, nil } return "", nil }
// setupHost runs the specified setup script for an individual host. Returns // the output from running the script remotely, as well as any error that // occurs. If the script exits with a non-zero exit code, the error will be non-nil. func (init *HostInit) setupHost(targetHost *host.Host) ([]byte, error) { // fetch the appropriate cloud provider for the host cloudMgr, err := providers.GetCloudManager(targetHost.Provider, init.Settings) if err != nil { return nil, fmt.Errorf("failed to get cloud manager for host %v with provider %v: %v", targetHost.Id, targetHost.Provider, err) } // mark the host as initializing if err := targetHost.SetInitializing(); err != nil { if err == mgo.ErrNotFound { return nil, ErrHostAlreadyInitializing } else { return nil, fmt.Errorf("database error: %v", err) } } // run the function scheduled for when the host is up err = cloudMgr.OnUp(targetHost) if err != nil { // if this fails it is probably due to an API hiccup, so we keep going. evergreen.Logger.Logf(slogger.WARN, "OnUp callback failed for host '%v': '%v'", targetHost.Id, err) } // run the remote setup script as sudo, if appropriate sudoStr := "" if targetHost.Distro.SetupAsSudo { sudoStr = "sudo " } // parse the hostname into the user, host and port hostInfo, err := util.ParseSSHInfo(targetHost.Host) if err != nil { return nil, err } user := targetHost.Distro.User if hostInfo.User != "" { user = hostInfo.User } // create a temp file for the setup script fileName := "setup.sh" file, err := ioutil.TempFile("", fileName) if err != nil { return nil, fmt.Errorf("error creating setup script: %v", err) } defer func() { file.Close() os.Remove(file.Name()) }() // build the setup script setup, err := init.buildSetupScript(targetHost) if err != nil { return nil, fmt.Errorf("error building setup script for host %v: %v", targetHost.Id, err) } // write the setup script to the file if _, err := file.Write([]byte(setup)); err != nil { return nil, fmt.Errorf("error writing remote setup script: %v", err) } cloudHost, err := providers.GetCloudHost(targetHost, init.Settings) if err != nil { return nil, fmt.Errorf("Failed to get cloud host for %v: %v", targetHost.Id, err) } sshOptions, err := cloudHost.GetSSHOptions() if err != nil { return nil, fmt.Errorf("Error getting ssh options for host %v: %v", targetHost.Id, err) } // copy setup script over to the remote machine var scpSetupCmdStderr bytes.Buffer scpSetupCmd := &command.ScpCommand{ Source: file.Name(), Dest: fileName, Stdout: &scpSetupCmdStderr, Stderr: &scpSetupCmdStderr, RemoteHostName: hostInfo.Hostname, User: user, Options: append([]string{"-P", hostInfo.Port}, sshOptions...), } // run the command to scp the setup script with a timeout err = util.RunFunctionWithTimeout( scpSetupCmd.Run, SCPTimeout, ) if err != nil { if err == util.ErrTimedOut { scpSetupCmd.Stop() return nil, fmt.Errorf("scp-ing setup script timed out") } return nil, fmt.Errorf("error (%v) copying setup script to remote "+ "machine: %v", err, scpSetupCmdStderr.String()) } // run command to ssh into remote machine and execute setup script var sshSetupCmdStderr bytes.Buffer runSetupCmd := &command.RemoteCommand{ CmdString: sudoStr + "sh " + fileName, Stdout: &sshSetupCmdStderr, Stderr: &sshSetupCmdStderr, RemoteHostName: hostInfo.Hostname, User: user, Options: []string{"-p", hostInfo.Port}, Background: false, } // only force creation of a tty if sudo if targetHost.Distro.SetupAsSudo { runSetupCmd.Options = []string{"-t", "-t", "-p", hostInfo.Port} } runSetupCmd.Options = append(runSetupCmd.Options, sshOptions...) // run the ssh command with given timeout err = util.RunFunctionWithTimeout( runSetupCmd.Run, time.Duration(SSHTimeoutSeconds)*time.Second, ) return sshSetupCmdStderr.Bytes(), err }