This repository has been archived by the owner on Jul 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Scott Bommarito
authored
Jul 30, 2018
1 parent
e7b9e8e
commit f18edac
Showing
56 changed files
with
2,747 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Microsoft.Extensions.Logging; | ||
using System; | ||
|
||
namespace NuGet.Jobs.Extensions | ||
{ | ||
public static class LoggerExtensions | ||
{ | ||
/// <summary> | ||
/// Calls <see cref="ILogger.BeginScope{TState}(TState)"/> and logs a message when entering and leaving the scope. | ||
/// </summary> | ||
public static IDisposable Scope( | ||
this ILogger logger, | ||
string message, | ||
params object[] args) | ||
{ | ||
return new LoggerScopeHelper(logger, message, args); | ||
} | ||
|
||
private class LoggerScopeHelper : IDisposable | ||
{ | ||
private readonly ILogger _logger; | ||
private readonly IDisposable _scope; | ||
|
||
private readonly string _message; | ||
private readonly object[] _args; | ||
|
||
private bool _isDisposed = false; | ||
|
||
public LoggerScopeHelper( | ||
ILogger logger, string message, object[] args) | ||
{ | ||
_logger = logger; | ||
_message = message; | ||
_args = args; | ||
|
||
_scope = logger.BeginScope(_message, _args); | ||
_logger.LogInformation("Entering scope: " + _message, _args); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
if (!_isDisposed) | ||
{ | ||
_logger.LogInformation("Leaving scope: " + _message, _args); | ||
_scope?.Dispose(); // ILogger can return a null scope (most notably during testing with a Mock<ILogger>) | ||
_isDisposed = true; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using NuGet.Jobs.Extensions; | ||
using NuGet.Services.Status.Table; | ||
using StatusAggregator.Table; | ||
|
||
namespace StatusAggregator | ||
{ | ||
public class Cursor : ICursor | ||
{ | ||
public Cursor( | ||
ITableWrapper table, | ||
ILogger<Cursor> logger) | ||
{ | ||
_table = table ?? throw new ArgumentNullException(nameof(table)); | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
} | ||
|
||
private readonly ITableWrapper _table; | ||
|
||
private readonly ILogger<Cursor> _logger; | ||
|
||
public async Task<DateTime> Get() | ||
{ | ||
using (_logger.Scope("Fetching cursor.")) | ||
{ | ||
var cursor = await _table.Retrieve<CursorEntity>( | ||
CursorEntity.DefaultPartitionKey, CursorEntity.DefaultRowKey); | ||
|
||
DateTime value; | ||
if (cursor == null) | ||
{ | ||
// If we can't find a cursor, the job is likely uninitialized, so start at the beginning of time. | ||
value = DateTime.MinValue; | ||
_logger.LogInformation("Could not fetch cursor, reinitializing cursor at {Cursor}.", value); | ||
} | ||
else | ||
{ | ||
value = cursor.Value; | ||
_logger.LogInformation("Fetched cursor with value {Cursor}.", value); | ||
} | ||
|
||
return value; | ||
} | ||
} | ||
|
||
public Task Set(DateTime value) | ||
{ | ||
using (_logger.Scope("Updating cursor to {Cursor}.", value)) | ||
{ | ||
var cursorEntity = new CursorEntity(value); | ||
return _table.InsertOrReplaceAsync(cursorEntity); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using NuGet.Jobs.Extensions; | ||
using NuGet.Services.Status.Table; | ||
using StatusAggregator.Table; | ||
|
||
namespace StatusAggregator | ||
{ | ||
public class EventUpdater : IEventUpdater | ||
{ | ||
public readonly TimeSpan _eventEndDelay; | ||
|
||
private readonly ITableWrapper _table; | ||
private readonly IMessageUpdater _messageUpdater; | ||
|
||
private readonly ILogger<EventUpdater> _logger; | ||
|
||
public EventUpdater( | ||
ITableWrapper table, | ||
IMessageUpdater messageUpdater, | ||
StatusAggregatorConfiguration configuration, | ||
ILogger<EventUpdater> logger) | ||
{ | ||
_table = table ?? throw new ArgumentNullException(nameof(table)); | ||
_messageUpdater = messageUpdater ?? throw new ArgumentNullException(nameof(messageUpdater)); | ||
_eventEndDelay = TimeSpan.FromMinutes(configuration?.EventEndDelayMinutes ?? throw new ArgumentNullException(nameof(configuration))); | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
} | ||
|
||
public async Task UpdateActiveEvents(DateTime cursor) | ||
{ | ||
using (_logger.Scope("Updating active events.")) | ||
{ | ||
var activeEvents = _table.GetActiveEvents().ToList(); | ||
_logger.LogInformation("Updating {ActiveEventsCount} active events.", activeEvents.Count()); | ||
foreach (var activeEvent in activeEvents) | ||
{ | ||
await UpdateEvent(activeEvent, cursor); | ||
} | ||
} | ||
} | ||
|
||
public async Task<bool> UpdateEvent(EventEntity eventEntity, DateTime cursor) | ||
{ | ||
eventEntity = eventEntity ?? throw new ArgumentNullException(nameof(eventEntity)); | ||
|
||
using (_logger.Scope("Updating event '{EventRowKey}' given cursor {Cursor}.", eventEntity.RowKey, cursor)) | ||
{ | ||
if (!eventEntity.IsActive) | ||
{ | ||
_logger.LogInformation("Event is inactive, cannot update."); | ||
return false; | ||
} | ||
|
||
var incidentsLinkedToEventQuery = _table.GetIncidentsLinkedToEvent(eventEntity); | ||
|
||
var incidentsLinkedToEvent = incidentsLinkedToEventQuery.ToList(); | ||
if (!incidentsLinkedToEvent.Any()) | ||
{ | ||
_logger.LogInformation("Event has no linked incidents and must have been created manually, cannot update."); | ||
return false; | ||
} | ||
|
||
var shouldDeactivate = !incidentsLinkedToEventQuery | ||
.Where(i => i.IsActive || i.MitigationTime > cursor - _eventEndDelay) | ||
.ToList() | ||
.Any(); | ||
|
||
if (shouldDeactivate) | ||
{ | ||
_logger.LogInformation("Deactivating event because its incidents are inactive and too old."); | ||
var mitigationTime = incidentsLinkedToEvent | ||
.Max(i => i.MitigationTime ?? DateTime.MinValue); | ||
eventEntity.EndTime = mitigationTime; | ||
|
||
await _messageUpdater.CreateMessageForEventStart(eventEntity, mitigationTime); | ||
await _messageUpdater.CreateMessageForEventEnd(eventEntity); | ||
|
||
// Update the event | ||
await _table.InsertOrReplaceAsync(eventEntity); | ||
} | ||
else | ||
{ | ||
_logger.LogInformation("Event has active or recent incidents so it will not be deactivated."); | ||
await _messageUpdater.CreateMessageForEventStart(eventEntity, cursor); | ||
} | ||
|
||
return shouldDeactivate; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace StatusAggregator | ||
{ | ||
/// <summary> | ||
/// Maintains the current progress of the job. | ||
/// </summary> | ||
public interface ICursor | ||
{ | ||
Task<DateTime> Get(); | ||
Task Set(DateTime value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using NuGet.Services.Status.Table; | ||
|
||
namespace StatusAggregator | ||
{ | ||
/// <summary> | ||
/// Handles updating any active <see cref="EventEntity"/>s. | ||
/// </summary> | ||
public interface IEventUpdater | ||
{ | ||
/// <summary> | ||
/// Updates all active <see cref="EventEntity"/>s. | ||
/// </summary> | ||
/// <param name="cursor">The current timestamp processed by the job.</param> | ||
Task UpdateActiveEvents(DateTime cursor); | ||
|
||
/// <summary> | ||
/// Update <paramref name="eventEntity"/> given <paramref name="cursor"/>. | ||
/// Determines whether or not to deactivate <paramref name="eventEntity"/> and updates any messages associated with the event. | ||
/// </summary> | ||
/// <param name="cursor">The current timestamp processed by the job.</param> | ||
/// <returns>Whether or not <paramref name="eventEntity"/> was deactivated.</returns> | ||
Task<bool> UpdateEvent(EventEntity eventEntity, DateTime cursor); | ||
} | ||
} |
Oops, something went wrong.