Example #1
0
        private static void ProcessNotify(GenericNotificationInfo info)
        {
            log.DebugFormat("Running notifiers for lane_id: {1}, revision_id: {2}, host_id: {3}, message: {0}", info.message, info.laneID, info.revisionID, info.hostID);

            try {
                lock (lock_obj) {
                    List <NotificationBase> notifiers;

                    if (!notifications_per_lane.TryGetValue(info.laneID, out notifiers))
                    {
                        return;
                    }

                    foreach (var notification in notifiers)
                    {
                        notification.NotifyGeneric(info);
                    }
                }
            } catch (Exception ex) {
                log.ErrorFormat("Exception while processing notification: {0}", ex);
            }
        }
        public override void NotifyGeneric(GenericNotificationInfo info)
        {
            string laneName, hostName, hash, repoURL;

            // Fetch info from database
            using (var db = new DB())
                using (var cmd = db.CreateCommand(@"
				SELECT lane.lane, host.host, revision.revision, lane.repository
				FROM revisionwork
				INNER JOIN lane ON lane.id = revisionwork.lane_id
				INNER JOIN host ON host.id = revisionwork.host_id
				INNER JOIN revision ON revision.id = revisionwork.revision_id
				WHERE revisionwork.lane_id = @laneid AND revisionwork.host_id = @hostid AND revisionwork.revision_id = @revisionid
			"            )) {
                    DB.CreateParameter(cmd, "laneid", info.laneID);
                    DB.CreateParameter(cmd, "hostid", info.hostID);
                    DB.CreateParameter(cmd, "revisionid", info.revisionID);

                    using (var reader = cmd.ExecuteReader()) {
                        if (!reader.Read())
                        {
                            log.WarnFormat("GitHub Notifier: RevisionWork for lane {0} host {1} revision {2} no longer exists.", info.laneID, info.hostID, info.revisionID);
                            return;
                        }

                        laneName = reader.GetString(0);
                        hostName = reader.GetString(1);
                        hash     = reader.GetString(2);
                        repoURL  = reader.GetString(3);
                    }
                }

            string message   = String.Format("Lane: {0}, Host: {1}: {2}", laneName, hostName, info.message);
            var    statusObj = createPayload(info.laneID, info.hostID, info.revisionID, message, STATE_TO_GITHUB[info.state]);

            sendNotification(repoURL, hash, statusObj);
        }
		public GetBuildInfoResponse GetBuildInfoMultiple (WebServiceLogin login, string host, bool multiple_work)
		{
			List<DBHost> hosts = new List<DBHost> (); // list of hosts to find work for
			List<DBHostLane> hostlanes = new List<DBHostLane> ();
			List<DBLane> lanes = new List<DBLane> ();

			GetBuildInfoResponse response = new GetBuildInfoResponse ();

			response.Work = new List<List<BuildInfoEntry>> ();

			using (DB db = new DB ()) {
				VerifyUserInRole (db, login, Roles.BuildBot, true);

				response.Host = FindHost (db, null, host);

				if (!response.Host.enabled)
					return response;

				// find the master hosts for this host (if any)
				response.MasterHosts = FindMasterHosts (db, response.Host);

				// get the hosts to find work for
				if (response.MasterHosts != null && response.MasterHosts.Count > 0) {
					foreach (DBMasterHost mh in response.MasterHosts)
						hosts.Add (DBHost_Extensions.Create (db, mh.master_host_id));
				} else {
					hosts.Add (response.Host);
				}

				// find the enabled hostlane combinations for these hosts
				using (IDbCommand cmd = db.CreateCommand ()) {
					cmd.CommandText = "SELECT HostLane.* FROM HostLane INNER JOIN Lane ON Lane.id = HostLane.lane_id WHERE Lane.enabled = TRUE AND HostLane.enabled = TRUE AND (";
					for (int i = 0; i < hosts.Count; i++) {
						if (i > 0)
							cmd.CommandText += " OR ";
						cmd.CommandText += " HostLane.host_id = " + hosts [i].id;
					}
					cmd.CommandText += ")";
					using (IDataReader reader = cmd.ExecuteReader ()) {
						while (reader.Read ())
							hostlanes.Add (new DBHostLane (reader));
					}
				}

				if (hostlanes.Count == 0)
					return response; // nothing to do here

				lanes = db.GetAllLanes ();

				switch (response.Host.QueueManagement) {
				case DBQueueManagement.OneRevisionWorkAtATime:
					if (hostlanes.Count > 1) {
						int latest = -1;
						DateTime latest_date = DateTime.MaxValue;

						// we need to find the latest revisionwork each hostlane has completed.
						// we want to work on the hostlane which has waited the longest amount
						// of time without getting work done (but which has pending work to do).

						for (int i = 0; i < hostlanes.Count; i++) {
							DBHostLane hl = hostlanes [i];
							// check if this hostlane has pending work.
							// this would ideally be included in the query below, but I'm not sure
							// how to do that while still distinguising the case where nothing has
							// been done ever for a hostlane.
							using (IDbCommand cmd = db.CreateCommand ()) {
								cmd.CommandText = @"
SELECT RevisionWork.id
FROM RevisionWork
WHERE
    RevisionWork.host_id = @host_id
AND (RevisionWork.workhost_id = @workhost_id OR RevisionWork.workhost_id IS NULL)
AND RevisionWork.completed = false
AND RevisionWork.state <> 9 AND RevisionWork.state <> 10 AND RevisionWork.state <> 11
AND lane_id = @lane_id
LIMIT 1;
    ";
								DB.CreateParameter (cmd, "lane_id", hl.lane_id);
								DB.CreateParameter (cmd, "host_id", hl.host_id);
								DB.CreateParameter (cmd, "workhost_id", response.Host.id);

								object obj = cmd.ExecuteScalar ();
								if (obj == DBNull.Value || obj == null) {
									// there is nothing to do for this hostlane
									continue;
								}

							}

							// find the latest completed (this may not be correct, maybe find the latest unstarted?)
							// revisionwork for this hostlane.
							using (IDbCommand cmd = db.CreateCommand ()) {
								cmd.CommandText = @"
SELECT 	RevisionWork.endtime
FROM RevisionWork
WHERE 
RevisionWork.host_id = @host_id
AND (RevisionWork.workhost_id = @workhost_id OR RevisionWork.workhost_id IS NULL)
AND RevisionWork.completed = true
AND lane_id = @lane_id
ORDER BY RevisionWork.endtime DESC
LIMIT 1;
";

								DB.CreateParameter (cmd, "lane_id", hl.lane_id);
								DB.CreateParameter (cmd, "host_id", hl.host_id);
								DB.CreateParameter (cmd, "workhost_id", response.Host.id);

								object obj = cmd.ExecuteScalar ();
								if (obj is DateTime) {
									DateTime dt = (DateTime) obj;
									if (dt < latest_date) {
										latest_date = dt;
										latest = i;
									}
								} else {
									// nothing has ever been done for this hostlane.
									latest_date = DateTime.MinValue;
									latest = i;
								}
							}

						}
						if (latest >= 0) {
							DBHostLane tmp = hostlanes [latest];
							hostlanes.Clear ();
							hostlanes.Add (tmp);
						} else {
							hostlanes.Clear (); // there is nothing to do at all
						}
					}
					break;
				}

				foreach (DBHostLane hl in hostlanes) {
					int counter = 10;
					DBRevisionWork revisionwork;
					DBLane lane = null;
					DBHost masterhost = null;

					foreach (DBLane l in lanes) {
						if (l.id == hl.lane_id) {
							lane = l;
							break;
						}
					}
					foreach (DBHost hh in hosts) {
						if (hh.id == hl.host_id) {
							masterhost = hh;
							break;
						}
					}

					do {
						revisionwork = db.GetRevisionWork (lane, masterhost, response.Host);
						if (revisionwork == null)
							break;
					} while (!revisionwork.SetWorkHost (db, response.Host) && counter-- > 0);

					if (revisionwork == null)
						continue;

					if (!revisionwork.workhost_id.HasValue || revisionwork.workhost_id != response.Host.id)
						continue; // couldn't lock this revisionwork.

					log.DebugFormat ("Found work for host {0} {4}: {1} (lane: {2} {3})", response.Host.id, revisionwork.id, revisionwork.lane_id, lane.lane, response.Host.host);

					DBRevision revision = DBRevision_Extensions.Create (db, revisionwork.revision_id);
					List<DBWorkFile> files_to_download = null;
					List<DBLane> dependent_lanes = null;

					// get dependent files
					List<DBLaneDependency> dependencies = lane.GetDependencies (db);
					if (dependencies != null && dependencies.Count > 0) {
						foreach (DBLaneDependency dep in dependencies) {
							DBLane dependent_lane;
							DBHost dependent_host;
							DBRevisionWork dep_revwork;
							List<DBWorkFile> work_files;

							if (string.IsNullOrEmpty (dep.download_files))
								continue;

							dependent_lane = DBLane_Extensions.Create (db, dep.dependent_lane_id);
							dependent_host = dep.dependent_host_id.HasValue ? DBHost_Extensions.Create (db, dep.dependent_host_id.Value) : null;
							DBRevision dep_lane_rev = dependent_lane.FindRevision (db, revision.revision);
							if (dep_lane_rev == null)
								continue; /* Something bad happened: the lane we're dependent on does not have the same revisions we have */
							dep_revwork = DBRevisionWork_Extensions.Find (db, dependent_lane, dependent_host, dep_lane_rev);

							work_files = dep_revwork.GetFiles (db);

							foreach (DBWorkFile file in work_files) {
								bool download = true;
								foreach (string exp in dep.download_files.Split (new char [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) {
									if (!System.Text.RegularExpressions.Regex.IsMatch (file.filename, FileUtilities.GlobToRegExp (exp))) {
										download = false;
										break;
									}
								}
								if (!download)
									continue;
								if (files_to_download == null) {
									files_to_download = new List<DBWorkFile> ();
									dependent_lanes = new List<DBLane> ();
								}
								files_to_download.Add (file);
								dependent_lanes.Add (dependent_lane);
							}
						}
					}


					List<DBWorkView2> pending_work = revisionwork.GetNextWork (db, lane, masterhost, revision, multiple_work);

					if (pending_work == null || pending_work.Count == 0)
						continue;

					List<DBEnvironmentVariable> environment_variables = null;
					using (IDbCommand cmd = db.CreateCommand ()) {
						foreach (int li in db.GetLaneHierarchy (lane.id)) {
							cmd.CommandText += string.Format (@"
SELECT * 
FROM EnvironmentVariable 
WHERE 
(host_id = {0} OR host_id = {1} OR host_id IS NULL) AND (lane_id = {2} OR lane_id IS NULL)
ORDER BY id;
;", revisionwork.workhost_id, revisionwork.host_id, li);
						}
						using (IDataReader reader = cmd.ExecuteReader ()) {
							var set = new HashSet<string> ();
							do {
								while (reader.Read ()) {
									if (environment_variables == null)
										environment_variables = new List<DBEnvironmentVariable> ();

									var ev = new DBEnvironmentVariable (reader);
									if (!set.Contains (ev.name)) {
										environment_variables.Add (ev);
										set.Add (ev.name);
									}
								}
							} while (reader.NextResult ());
						}
					}

					DBHost host_being_worked_for = hosts.Find (h => h.id == revisionwork.host_id);

					foreach (DBWorkView2 work in pending_work) {
						BuildInfoEntry entry = new BuildInfoEntry ();
						entry.Lane = lane;
						entry.HostLane = hl;
						entry.Revision = revision;
						entry.Command = DBCommand_Extensions.Create (db, work.command_id);
						entry.FilesToDownload = files_to_download;
						entry.DependentLaneOfFiles = dependent_lanes;
						entry.Work = DBWork_Extensions.Create (db, work.id);
						entry.LaneFiles = lane.GetFiles (db, lanes);
						entry.EnvironmentVariables = environment_variables;
						entry.Host = host_being_worked_for;

						// TODO: put work with the same sequence number into one list of entries.
						List<BuildInfoEntry> entries = new List<BuildInfoEntry> ();
						entries.Add (entry);
						response.Work.Add (entries);
					}

					// Notify that the revision is assigned
					var notifyInfo = new GenericNotificationInfo ();
					notifyInfo.laneID = revisionwork.lane_id;
					notifyInfo.hostID = revisionwork.host_id;
					notifyInfo.revisionID = revisionwork.revision_id;
					notifyInfo.message = String.Format("Assigned to host '{0}' ({1})", response.Host.host, response.Host.id);
					notifyInfo.state = DBState.Executing;

					Notifications.NotifyGeneric (notifyInfo);
				}
			}

			return response;
		}
		public ReportBuildStateResponse ReportBuildState (WebServiceLogin login, DBWork work)
		{
			ReportBuildStateResponse response = new ReportBuildStateResponse ();

			using (DB db = new DB ())
			using (var transaction = db.BeginTransaction ()) {
				VerifyUserInRole (db, login, Roles.BuildBot, true);
				log.DebugFormat ("ReportBuildState, state: {0}, start time: {1}, end time: {2}", work.State, work.starttime, work.endtime);
				if (work.starttime > new DateTime (2000, 1, 1) && work.endtime < work.starttime) {
					// the issue here is that the server interprets the datetime as local time, while it's always as utc.
					try {
						using (IDbCommand cmd = db.CreateCommand ()) {
							cmd.CommandText = "SELECT starttime FROM Work WHERE id = " + work.id;
							var value = cmd.ExecuteScalar ();
							if (value is DateTime)
								work.starttime = (DateTime) value;
						}
					} catch (Exception ex) {
						log.ErrorFormat ("ReportBuildState: Exception while fixing timezone data: {0}", ex);
					}
				}
				work.Save (db);
				work.Reload (db);

				response.Work = work;

				DBRevisionWork rw = DBRevisionWork_Extensions.Create (db, work.revisionwork_id);
				bool was_completed = rw.completed;
				rw.UpdateState (db);

				if (rw.startedtime == null) {
					rw.startedtime = work.starttime;
					rw.Save (db);
				}

				if (!was_completed && rw.completed) {
					rw.endtime = DBRecord.DatabaseNow;
					rw.Save (db);
				}

				Notifications.Notify (work, rw);

				if (!was_completed && rw.completed) {
					var notifyInfo = new GenericNotificationInfo ();
					notifyInfo.laneID = rw.lane_id;
					notifyInfo.hostID = rw.host_id;
					notifyInfo.revisionID = rw.revision_id;
					notifyInfo.message = "Completed";
					notifyInfo.state = rw.State;

					Notifications.NotifyGeneric (notifyInfo);
				}
				response.RevisionWorkCompleted = rw.completed;

				using (var cmd = db.CreateCommand ()) {
					cmd.CommandText = "UPDATE Lane SET changed_date = @date WHERE id = @id;";
					DB.CreateParameter (cmd, "date", DateTime.UtcNow);
					DB.CreateParameter (cmd, "id", rw.lane_id);
					cmd.ExecuteNonQuery ();
				}

				// Check if any other lane depends on this one
				if (response.RevisionWorkCompleted) {
					using (IDbCommand cmd = db.CreateCommand ()) {
						cmd.CommandText = "SELECT 1 FROM LaneDependency WHERE dependent_lane_id = @lane_id LIMIT 1;";
						DB.CreateParameter (cmd, "lane_id", rw.lane_id);

						object value = cmd.ExecuteScalar ();
						if (value is int) {
							// If so, run the scheduler
							MonkeyWrench.Scheduler.Scheduler.ExecuteSchedulerAsync (false);
						}
					}
				}

				transaction.Commit ();
				return response;
			}
		}
		public override void NotifyGeneric (GenericNotificationInfo info)
		{
			string laneName, hostName, hash, repoURL;

			// Fetch info from database
			using (var db = new DB ())
			using (var cmd = db.CreateCommand (@"
				SELECT lane.lane, host.host, revision.revision, lane.repository
				FROM revisionwork
				INNER JOIN lane ON lane.id = revisionwork.lane_id
				INNER JOIN host ON host.id = revisionwork.host_id
				INNER JOIN revision ON revision.id = revisionwork.revision_id
				WHERE revisionwork.lane_id = @laneid AND revisionwork.host_id = @hostid AND revisionwork.revision_id = @revisionid
			")) {
				DB.CreateParameter (cmd, "laneid", info.laneID);
				DB.CreateParameter (cmd, "hostid", info.hostID);
				DB.CreateParameter (cmd, "revisionid", info.revisionID);

				using (var reader = cmd.ExecuteReader ()) {
					if (!reader.Read ()) {
						log.WarnFormat ("GitHub Notifier: RevisionWork for lane {0} host {1} revision {2} no longer exists.", info.laneID, info.hostID, info.revisionID);
						return;
					}

					laneName = reader.GetString (0);
					hostName = reader.GetString (1);
					hash = reader.GetString (2);
					repoURL = reader.GetString (3);
				}
			}

			string message = String.Format ("Lane: {0}, Host: {1}: {2}", laneName, hostName, info.message);
			var statusObj = createPayload (info.laneID, info.hostID, info.revisionID, message, STATE_TO_GITHUB[info.state]);
			sendNotification (repoURL, hash, statusObj);
		}
Example #6
0
		/// <summary>
		/// Returns true if something was added to the database.
		/// </summary>
		/// <param name="db"></param>
		/// <param name="lane"></param>
		/// <param name="host"></param>
		/// <returns></returns>
		public static bool AddRevisionWork (DB db)
		{
			var stopwatch = new Stopwatch ();
			stopwatch.Start ();
			int line_count = 0;

			try {
				using (var cmd = db.CreateCommand (@"
					INSERT INTO RevisionWork (lane_id, host_id, revision_id, state)
					SELECT Lane.id, Host.id, Revision.id, 10
					FROM HostLane
					INNER JOIN Host ON HostLane.host_id = Host.id
					INNER JOIN Lane ON HostLane.lane_id = Lane.id
					INNER JOIN Revision ON Revision.lane_id = lane.id
					WHERE HostLane.enabled = true AND
						NOT EXISTS (
							SELECT 1
							FROM RevisionWork 
							WHERE RevisionWork.lane_id = Lane.id AND RevisionWork.host_id = Host.id AND RevisionWork.revision_id = Revision.id
							)
					RETURNING lane_id, host_id, revision_id
				"))
				using (IDataReader reader = cmd.ExecuteReader ()) {
					while (reader.Read ()) {
						int lane_id = reader.GetInt32 (0);
						int host_id = reader.GetInt32 (1);
						int revision_id = reader.GetInt32 (2);

						var info = new GenericNotificationInfo(); 
						info.laneID = lane_id;
						info.hostID = host_id;
						info.revisionID = revision_id;
						info.message = "Commit received.";
						info.state = DBState.Executing;

						Notifications.NotifyGeneric (info);

						line_count++;
					}
				}
				log.DebugFormat ("AddRevisionWork: Added {0} records.", line_count);
				return line_count > 0;
			} catch (Exception ex) {
				log.ErrorFormat ("AddRevisionWork got an exception: {0}", ex);
				return false;
			} finally {
				stopwatch.Stop ();
				log.InfoFormat ("AddRevisionWork [Done in {0} seconds]", stopwatch.Elapsed.TotalSeconds);
			}
		}
Example #7
0
 /**
  * Used for notifications not tied to a specific work. (ex. revision downloaded or assigned)
  */
 public virtual void NotifyGeneric(GenericNotificationInfo info)
 {
 }
Example #8
0
 public static void NotifyGeneric(GenericNotificationInfo info)
 {
     queued_notifications.Add(new QueuedNotification {
         IsInfo = true, info = info
     });
 }
Example #9
0
		/// <summary>
		/// Returns true if something was added to the database.
		/// </summary>
		/// <param name="db"></param>
		/// <param name="lanes"></param>
		/// <param name="hostlanes"></param>
		/// <returns></returns>
		public static bool AddRevisionWorkSlow (DB db, List<DBLane> lanes, List<DBHostLane> hostlanes)
		{
			var stopwatch = new Stopwatch ();
			stopwatch.Start ();
			int line_count = 0;

			try {
				var selected_lanes = new Dictionary<int, DBLane> ();
				foreach (var hl in hostlanes) {
					if (!hl.enabled)
						continue;

					if (!selected_lanes.ContainsKey (hl.lane_id))
						selected_lanes [hl.lane_id] = lanes.Find ((v) => v.id == hl.lane_id);
				}
				foreach (var l in lanes) {
					if (l.enabled)
						continue;
					if (selected_lanes.ContainsKey (l.id))
						selected_lanes.Remove (l.id);
				}
				foreach (var id in selected_lanes.Keys) {
					using (var cmd = db.CreateCommand (string.Format (@"
						INSERT INTO RevisionWork (lane_id, host_id, revision_id, state)
						SELECT Lane.id, Host.id, Revision.id, 10
						FROM HostLane
						INNER JOIN Host ON HostLane.host_id = Host.id
						INNER JOIN Lane ON HostLane.lane_id = Lane.id
						INNER JOIN Revision ON Revision.lane_id = lane.id
						WHERE HostLane.enabled = true AND Lane.id = {0} AND
							NOT EXISTS (
								SELECT 1
								FROM RevisionWork 
								WHERE RevisionWork.lane_id = Lane.id AND RevisionWork.host_id = Host.id AND RevisionWork.revision_id = Revision.id
								)
						RETURNING lane_id, host_id, revision_id
					", id)))
					using (IDataReader reader = cmd.ExecuteReader ()) {
						while (reader.Read ()) {
							int lane_id = reader.GetInt32 (0);
							int host_id = reader.GetInt32 (1);
							int revision_id = reader.GetInt32 (2);

							var info = new GenericNotificationInfo(); 
							info.laneID = lane_id;
							info.hostID = host_id;
							info.revisionID = revision_id;
							info.message = "Commit received.";
							info.state = DBState.Executing;

							Notifications.NotifyGeneric (info);

							line_count++;
						}
					}
					log.DebugFormat ("AddRevisionWorkSlow: Added {0} records for lane {1}.", line_count, selected_lanes [id].lane);
				}
				return line_count > 0;
			} catch (Exception ex) {
				log.ErrorFormat ("AddRevisionWorkSlow got an exception: {0}", ex);
				return false;
			} finally {
				stopwatch.Stop ();
				log.InfoFormat ("AddRevisionWorkSlow [Done in {0} seconds]", stopwatch.Elapsed.TotalSeconds);
			}
		}
Example #10
0
		/**
		 * Used for notifications not tied to a specific work. (ex. revision downloaded or assigned)
		 */
		public virtual void NotifyGeneric(GenericNotificationInfo info) {}
Example #11
0
		private static void ProcessNotify(GenericNotificationInfo info)
		{
			log.DebugFormat ("Running notifiers for lane_id: {1}, revision_id: {2}, host_id: {3}, message: {0}", info.message, info.laneID, info.revisionID, info.hostID);

			try {
				lock (lock_obj) {
					List<NotificationBase> notifiers;

					if (!notifications_per_lane.TryGetValue (info.laneID, out notifiers)) {
						return;
					}

					foreach (var notification in notifiers) {
						notification.NotifyGeneric (info);
					}
				}
			} catch (Exception ex) {
				log.ErrorFormat ("Exception while processing notification: {0}", ex);
			}
		}
Example #12
0
		public static void NotifyGeneric (GenericNotificationInfo info)
		{
			queued_notifications.Add (new QueuedNotification { IsInfo = true, info = info });
		}