/// <summary> /// Notifies the host which lights to turn on or off. /// </summary> /// <param name="user">The user.</param> /// <param name="command">The command.</param> private void NotifyHost(User user, string command) { if (string.IsNullOrEmpty(user.Hostname)) { log.Debug(string.Format("Skipping notification for user {0}: Host not registered ({1})", user.Username, command)); return; } // TODO: Improve by dropping onto a queue to give it a more controlled async behaviour log.Info(string.Format("Sending command {0} to host {1}:{2} (user {3})", command, user.Hostname, this.clientPort, user.Username)); Thread thread = new Thread(() => { bool success = Utils.SendCommand(user.Hostname, this.clientPort, command); if (!success) { log.Warn("Could not send command to host"); } }); thread.IsBackground = true; thread.Start(); }
public void TestHandleUpdate() { NotificationManager notificationManager = null; UserCache userCache = null; try { // Setup the basics object syncRoot = new object(); int serverPort = 30001; int clientPort = 30002; userCache = new UserCache(); notificationManager = new NotificationManager(serverPort, clientPort, userCache, false); string buildKey1 = "buildkey1"; string buildKey2 = "buildkey2"; string username = "******"; string hostname = "localhost"; // Create a user that has one build building and is responsible for one other build User user = new User(username) { Hostname = hostname }; user.ActiveBuilds.Add(buildKey1); user.BuildsResponsibleFor.Add(buildKey2); // A dummy client which the notification manager will notify Listener listener = new Listener(IPAddress.Any, clientPort); Dictionary<RequestType, IRequest> requests = new Dictionary<RequestType, IRequest>(); listener.OnCommandReceived += (sender, args) => { requests.Add(((IRequest)sender).Type, (IRequest)sender); lock (syncRoot) { Monitor.Pulse(syncRoot); } }; listener.Start(); Assert.That(listener.Running, Is.True); // Raise an update for this user and wait until all events caused have been raised lock (syncRoot) { notificationManager.HandleUpdate(user, EventArgs.Empty); ////Monitor.Wait(syncRoot, 5000); Monitor.Wait(syncRoot, 5000); } listener.Stop(); Assert.That(listener.Running, Is.False); // Test Assert.That(requests.ContainsKey(RequestType.BuildActive)); BuildActiveRequest buildActiveRequest = (BuildActiveRequest)requests[RequestType.BuildActive]; Assert.That(buildActiveRequest.IsBuildsActive, Is.True); } finally { if (notificationManager != null) { notificationManager.Dispose(); } if (userCache != null) { userCache.Dispose(); } } }
/// <summary> /// Tries to get the user. If the user doesn't exist, it will be created. /// </summary> /// <param name="username">The username.</param> /// <returns>The user if found or a new user</returns> private User TryGetUser(string username) { User user; if (!this.userCache.TryGetValue(username.ToLower(), out user) || user == null) { user = new User(username.ToLower()); } return user; }
/// <summary> /// Updates the builds the user is responsible for. /// </summary> /// <param name="buildKey">The build key.</param> /// <param name="responsibilityNotification">The responsibility notification.</param> /// <param name="user">The user.</param> private void UpdateUserBuildsResponsibleFor(string buildKey, ResponsibilityNotification responsibilityNotification, ref User user) { if (Utils.IsAttentionRequired(responsibilityNotification.Type, responsibilityNotification.State)) { if (!user.BuildsResponsibleFor.Contains(buildKey)) { // Only one user can be responsible for a given build key foreach (User otherUser in this.userCache.Values.Where(x => x.BuildsResponsibleFor.Contains(buildKey))) { otherUser.BuildsResponsibleFor.Remove(buildKey); if (otherUser.BuildsResponsibleFor.Count == 0) { CancelPriority(otherUser); } } if (user.BuildsResponsibleFor.Count == 0) { user.AttentionFirstRequired = DateTime.Now; } // We could be removing the build key we just added, so this needs to happen afterwards user.BuildsResponsibleFor.Add(buildKey); } } else { user.BuildsResponsibleFor.Remove(buildKey); if (user.BuildsResponsibleFor.Count == 0) { CancelPriority(user); } } }
/// <summary> /// Notifies that there was an update. /// </summary> /// <param name="user">The user that was updated.</param> private void NotifyUpdate(User user) { if (this.OnUpdate != null) { this.OnUpdate(user, EventArgs.Empty); } }
/// <summary> /// Updates the builds the user is responsible for. /// </summary> /// <param name="buildKey">The build key.</param> /// <param name="buildNotification">The build notification.</param> /// <param name="user">The user.</param> private static void UpdateUserBuildsResponsibleFor(string buildKey, BuildNotification buildNotification, ref User user) { // Active vs. inactive build is mutually exclusive, but unfortunately not attention when it is // a build notification. Since IsAttentionRequired tests for build failures, it's negation doesn't // imply success. E.g. if the build is not failed, failed to start, failing or hanging, it doesn't // imply success. This is because we're mixing these with responsibility notifications, which // make it ambiguous. So, we need to actively test that attention can be cancelled. if (Utils.IsAttentionRequired(buildNotification.Type)) { // If this is the first notification of responsibility if (user.BuildsResponsibleFor.Count == 0) { user.AttentionFirstRequired = DateTime.Now; } if (!user.BuildsResponsibleFor.Contains(buildKey)) { user.BuildsResponsibleFor.Add(buildKey); } } else if (Utils.IsNoAttentionRequired(buildNotification.Type)) { user.BuildsResponsibleFor.Remove(buildKey); if (user.BuildsResponsibleFor.Count == 0) { CancelPriority(user); } } }
/// <summary> /// Updates the user's active builds. /// </summary> /// <param name="buildKey">The build key.</param> /// <param name="buildNotification">The build notification.</param> /// <param name="user">The user.</param> private static void UpdateUserActiveBuilds(string buildKey, BuildNotification buildNotification, ref User user) { // Active and inactive is mutually exclusive, i.e. if a build is not building (active) it must consequently // be inactive. if (Utils.IsActiveBuild(buildNotification.Type)) { if (!user.ActiveBuilds.Contains(buildKey)) { user.ActiveBuilds.Add(buildKey); } } else { user.ActiveBuilds.Remove(buildKey); } }
/// <summary> /// Cancels the priority. /// </summary> /// <param name="user">The user.</param> private static void CancelPriority(User user) { user.AttentionFirstRequired = null; user.IsAttentionPriority = false; }
/// <summary> /// Registers the user. /// </summary> /// <param name="request">The request.</param> public void Register(RegistrationRequest request) { User user = new User(request.Username) { Hostname = request.Hostname }; this.userCache.AddOrUpdate(user.Username, user, (username, existingUser) => { existingUser.Hostname = request.Hostname; return existingUser; }); this.userCache.TryGetValue(user.Username, out user); this.NotifyUpdate(user); }