Example #1
0
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, "")
}
Example #2
0
// 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
}
Example #3
0
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})
}
Example #4
0
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

}