func (as *APIServer) requestHost(w http.ResponseWriter, r *http.Request) { user := MustHaveUser(r) hostRequest := struct { Distro string `json:"distro"` PublicKey string `json:"public_key"` UserData string `json:"userdata"` }{} err := util.ReadJSONInto(r.Body, &hostRequest) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if hostRequest.Distro == "" { http.Error(w, "distro may not be blank", http.StatusBadRequest) return } if hostRequest.PublicKey == "" { http.Error(w, "public key may not be blank", http.StatusBadRequest) return } opts := spawn.Options{ Distro: hostRequest.Distro, UserName: user.Id, PublicKey: hostRequest.PublicKey, UserData: hostRequest.UserData, } spawner := spawn.New(&as.Settings) err = spawner.Validate(opts) if err != nil { errCode := http.StatusBadRequest if _, ok := err.(spawn.BadOptionsErr); !ok { errCode = http.StatusInternalServerError } as.LoggedError(w, r, errCode, fmt.Errorf("Spawn request failed validation: %v", err)) return } err = spawner.CreateHost(opts, user) if err != nil { evergreen.Logger.Logf(slogger.ERROR, err.Error()) mailErr := notify.TrySendNotificationToUser(opts.UserName, "Spawning failed", err.Error(), notify.ConstructMailer(as.Settings.Notify)) if mailErr != nil { evergreen.Logger.Logf(slogger.ERROR, "Failed to send notification: %v", mailErr) } return } as.WriteJSON(w, http.StatusOK, "") }
// send all of the specified notifications, and execute the callbacks for any // that are successfully sent. returns an aggregate list of any errors // that occur func sendNotifications(notifications []notification, settings *evergreen.Settings) []error { evergreen.Logger.Logf(slogger.INFO, "Sending %v notifications...", len(notifications)) // used to store any errors that occur var errors []error // ask for the mailer we'll use mailer := notify.ConstructMailer(settings.Notify) for _, n := range notifications { // send the notification err := notify.TrySendNotificationToUser( n.recipient, n.subject, n.message, mailer, ) // continue on error to allow further notifications to be sent if err != nil { errors = append(errors, fmt.Errorf("error sending notification"+ " to %v: %v", n.recipient, err)) continue } // run the notification's callback, since it has been successfully sent if n.callback != nil { if err := n.callback(n.host, n.threshold); err != nil { errors = append(errors, fmt.Errorf("error running notification"+ " callback: %v", err)) } } } return errors }
func (as *APIServer) spawnHostReady(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) instanceId := vars["instance_id"] status := vars["status"] // mark the host itself as provisioned host, err := host.FindOne(host.ById(instanceId)) if err != nil { as.LoggedError(w, r, http.StatusInternalServerError, err) return } if host == nil { http.Error(w, "host not found", http.StatusNotFound) return } if status == evergreen.HostStatusSuccess { if err := host.SetRunning(); err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error marking host id %v as %v: %v", instanceId, evergreen.HostStatusSuccess, err) } } else { alerts.RunHostProvisionFailTriggers(host) if err = host.SetDecommissioned(); err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error marking host %v for user %v as decommissioned: %v", host.Host, host.StartedBy, err) } evergreen.Logger.Logf(slogger.INFO, "Decommissioned %v for user %v because provisioning failed", host.Host, host.StartedBy) // send notification to the Evergreen team about this provisioning failure subject := fmt.Sprintf("%v Spawn provisioning failure on %v", notify.ProvisionFailurePreface, host.Distro.Id) message := fmt.Sprintf("Provisioning failed on %v host %v for user %v", host.Distro.Id, host.Host, host.StartedBy) if err = notify.NotifyAdmins(subject, message, &as.Settings); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error sending email: %v", err) } // get/store setup logs setupLog, err := ioutil.ReadAll(r.Body) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, fmt.Sprintf("error reading request: %v", err)) as.LoggedError(w, r, http.StatusInternalServerError, err) return } event.LogProvisionFailed(instanceId, string(setupLog)) } message := fmt.Sprintf(` Host with id %v spawned. The host's dns name is %v. To ssh in: ssh -i <your private key> %v@%v`, host.Id, host.Host, host.User, host.Host) if status == evergreen.HostStatusFailed { message += fmt.Sprintf("\nUnfortunately, the host's setup script did not run fully - check the setup.log " + "file in the machine's home directory to see more details") } err = notify.TrySendNotificationToUser(host.StartedBy, "Your host is ready", message, notify.ConstructMailer(as.Settings.Notify)) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error sending email: %v", err) } as.WriteJSON(w, http.StatusOK, spawnResponse{HostInfo: *host}) }
func (uis *UIServer) requestNewHost(w http.ResponseWriter, r *http.Request) { authedUser := MustHaveUser(r) putParams := struct { Distro string `json:"distro"` KeyName string `json:"key_name"` PublicKey string `json:"public_key"` SaveKey bool `json:"save_key"` UserData string `json:"userdata"` }{} if err := util.ReadJSONInto(r.Body, &putParams); err != nil { http.Error(w, fmt.Sprintf("Bad json in request: %v", err), http.StatusBadRequest) return } opts := spawn.Options{ Distro: putParams.Distro, UserName: authedUser.Username(), PublicKey: putParams.PublicKey, UserData: putParams.UserData, } spawner := spawn.New(&uis.Settings) if err := spawner.Validate(opts); err != nil { errCode := http.StatusBadRequest if _, ok := err.(spawn.BadOptionsErr); !ok { errCode = http.StatusInternalServerError } uis.LoggedError(w, r, errCode, err) return } // save the supplied public key if needed if putParams.SaveKey { dbuser, err := user.FindOne(user.ById(authedUser.Username())) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error fetching user: %v", err)) return } err = model.AddUserPublicKey(dbuser.Id, putParams.KeyName, putParams.PublicKey) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error saving public key: %v", err)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Public key successfully saved.")) } // Start a background goroutine that handles host creation/setup. go func() { host, err := spawner.CreateHost(opts) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "error spawning host: %v", err) mailErr := notify.TrySendNotificationToUser(authedUser.Email(), fmt.Sprintf("Spawning failed"), err.Error(), notify.ConstructMailer(uis.Settings.Notify)) if mailErr != nil { evergreen.Logger.Logf(slogger.ERROR, "Failed to send notification: %v", mailErr) } if host != nil { // a host was inserted - we need to clean it up dErr := host.SetDecommissioned() if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Failed to set host %v decommissioned: %v", host.Id, dErr) } } return } }() PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Host spawned")) uis.WriteJSON(w, http.StatusOK, "Host successfully spawned") return }