Skip to content

Commit 6adf07c

Browse files
missymessaCopilot
andauthored
Modify Grafana AlertHookController to create AzDO work items instead of GitHub issues (#6578)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 862301c commit 6adf07c

13 files changed

Lines changed: 507 additions & 273 deletions

File tree

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
using System;
2-
using System.Reflection;
2+
using System.Collections.Generic;
3+
using System.Collections.Immutable;
34
using AwesomeAssertions;
45
using DotNet.Status.Web.Controllers;
56
using DotNet.Status.Web.Models;
67
using DotNet.Status.Web.Options;
7-
using Microsoft.DotNet.GitHub.Authentication;
8+
using Microsoft.DotNet.Internal.AzureDevOps;
9+
using Microsoft.DotNet.Internal.DependencyInjection;
810
using Microsoft.Extensions.Logging.Abstractions;
911
using Microsoft.Extensions.Options;
1012
using Moq;
1113
using NUnit.Framework;
12-
using Octokit;
1314

1415
namespace DotNet.Status.Web.Tests;
1516

1617
[TestFixture]
1718
public class AlertHookControllerTests
1819
{
1920
[Test]
20-
public void GenerateNewIssue_WithMissingEvalMatchesAndNotificationTargets_DoesNotThrow()
21+
public void GenerateDescription_WithMissingEvalMatches_DoesNotThrow()
2122
{
22-
AlertHookController controller = CreateController(null);
23+
AlertHookController controller = CreateController();
2324
GrafanaNotification notification = new GrafanaNotification
2425
{
2526
Title = "Alert title",
@@ -29,19 +30,19 @@ public void GenerateNewIssue_WithMissingEvalMatchesAndNotificationTargets_DoesNo
2930
EvalMatches = null,
3031
};
3132

32-
Action action = () => InvokeGenerateNewIssue(controller, notification);
33+
Action action = () => controller.GenerateDescription(notification);
3334

3435
action.Should().NotThrow();
3536

36-
NewIssue issue = InvokeGenerateNewIssue(controller, notification);
37-
issue.Body.Should().Contain("Please investigate");
38-
issue.Body.Should().NotContain(", please investigate");
37+
string description = controller.GenerateDescription(notification);
38+
description.Should().Contain("Supplemental text");
39+
description.Should().Contain("Grafana-Automated-Alert-Id-");
3940
}
4041

4142
[Test]
42-
public void GenerateNewNotificationComment_WithMissingEvalMatches_DoesNotThrow()
43+
public void GenerateComment_WithMissingEvalMatches_DoesNotThrow()
4344
{
44-
AlertHookController controller = CreateController(Array.Empty<string>());
45+
AlertHookController controller = CreateController();
4546
GrafanaNotification notification = new GrafanaNotification
4647
{
4748
Title = "Alert title",
@@ -51,58 +52,76 @@ public void GenerateNewNotificationComment_WithMissingEvalMatches_DoesNotThrow()
5152
EvalMatches = null,
5253
};
5354

54-
Action action = () => InvokeGenerateNewNotificationComment(controller, notification);
55+
Action action = () => controller.GenerateComment(notification);
5556

5657
action.Should().NotThrow();
5758

58-
string comment = InvokeGenerateNewNotificationComment(controller, notification);
59-
comment.Should().Contain("Metric state changed to *alerting*");
59+
string comment = controller.GenerateComment(notification);
60+
comment.Should().Contain("Metric state changed to");
61+
comment.Should().Contain("alerting");
6062
}
6163

62-
private static AlertHookController CreateController(string[] notificationTargets)
64+
[Test]
65+
public void GenerateTitle_WithPrefix_PrependsPrefixToTitle()
6366
{
64-
Mock<IGitHubTokenProvider> tokenProvider = new(MockBehavior.Strict);
65-
IOptions<GitHubConnectionOptions> githubOptions = Microsoft.Extensions.Options.Options.Create(new GitHubConnectionOptions
67+
AlertHookController controller = CreateController();
68+
GrafanaNotification notification = new GrafanaNotification
6669
{
67-
Organization = "dotnet",
68-
Repository = "dnceng",
69-
NotificationTargets = notificationTargets,
70-
AlertLabels = Array.Empty<string>(),
71-
EnvironmentLabels = Array.Empty<string>(),
70+
Title = "CPU High",
71+
State = "alerting",
72+
};
73+
74+
string title = controller.GenerateTitle(notification);
75+
76+
title.Should().Be("[test] CPU High");
77+
}
78+
79+
[Test]
80+
public void GenerateDescription_WithEvalMatches_IncludesMetrics()
81+
{
82+
AlertHookController controller = CreateController();
83+
GrafanaNotification notification = new GrafanaNotification
84+
{
85+
Title = "Alert title",
86+
State = "alerting",
87+
Message = "High CPU",
88+
RuleUrl = "https://example/rule",
89+
EvalMatches = new List<GrafanaNotificationMatch>
90+
{
91+
new GrafanaNotificationMatch { Metric = "cpu_usage", Value = 95.5 },
92+
}.ToImmutableList(),
93+
};
94+
95+
string description = controller.GenerateDescription(notification);
96+
97+
description.Should().Contain("cpu_usage");
98+
description.Should().Contain("95.5");
99+
}
100+
101+
private static AlertHookController CreateController()
102+
{
103+
Mock<IAzureDevOpsClient> azureDevOpsClient = new(MockBehavior.Strict);
104+
Mock<IClientFactory<IAzureDevOpsClient>> clientFactory = new(MockBehavior.Strict);
105+
IOptions<AzureDevOpsAlertOptions> alertOptions = Microsoft.Extensions.Options.Options.Create(new AzureDevOpsAlertOptions
106+
{
107+
Organization = "dnceng",
108+
Project = "internal",
109+
AreaPath = @"internal\.NET Engineering Services\First Responders",
110+
WorkItemType = "DNCENG Task",
72111
TitlePrefix = "[test] ",
73112
SupplementalBodyText = "Supplemental text",
74113
});
75-
IOptions<GitHubClientOptions> clientOptions = Microsoft.Extensions.Options.Options.Create(new GitHubClientOptions
76-
{
77-
ProductHeader = new ProductHeaderValue("DotNetStatusWebTests"),
78-
});
79114

80115
IOptions<GrafanaOptions> grafanaOptions = Microsoft.Extensions.Options.Options.Create(new GrafanaOptions
81116
{
82117
WebhookSecret = "test-secret",
83118
});
84119

85120
return new AlertHookController(
86-
tokenProvider.Object,
87-
githubOptions,
88-
clientOptions,
121+
clientFactory.Object,
122+
alertOptions,
89123
grafanaOptions,
90124
NullLogger<AlertHookController>.Instance);
91125
}
92-
93-
private static NewIssue InvokeGenerateNewIssue(AlertHookController controller, GrafanaNotification notification)
94-
{
95-
MethodInfo method = typeof(AlertHookController).GetMethod("GenerateNewIssue", BindingFlags.Instance | BindingFlags.NonPublic);
96-
method.Should().NotBeNull();
97-
98-
return (NewIssue)method.Invoke(controller, new object[] { notification });
99-
}
100-
101-
private static string InvokeGenerateNewNotificationComment(AlertHookController controller, GrafanaNotification notification)
102-
{
103-
MethodInfo method = typeof(AlertHookController).GetMethod("GenerateNewNotificationComment", BindingFlags.Instance | BindingFlags.NonPublic);
104-
method.Should().NotBeNull();
105-
106-
return (string)method.Invoke(controller, new object[] { notification });
107-
}
108126
}
127+

src/DotNet.Status.Web/DotNet.Status.Web/.config/settings.Production.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"EnvironmentLabels": [ "Production" ],
1313
"TitlePrefix": "Production - "
1414
},
15+
"AzureDevOpsAlert": {
16+
"TitlePrefix": "Production - ",
17+
"SupplementalBodyText": "This alert was auto-generated by Grafana monitoring in the Production environment."
18+
},
1519
"DataProtection": {
1620
"KeyBlobUri": "https://dotnetengstatusprod.blob.core.windows.net/site/keys.xml",
1721
"DataProtectionKeyUri": "https://dotneteng-status-prod.vault.azure.net/keys/dotnet-status-data-protection/"

src/DotNet.Status.Web/DotNet.Status.Web/.config/settings.Staging.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
"EnvironmentLabels": [ "Staging" ],
1212
"TitlePrefix": "Staging - "
1313
},
14+
"AzureDevOpsAlert": {
15+
"TitlePrefix": "Staging - ",
16+
"SupplementalBodyText": "This alert was auto-generated by Grafana monitoring in the Staging environment."
17+
},
1418
"DataProtection": {
1519
"KeyBlobUri": "https://dotnetengstatusstaging.blob.core.windows.net/site/keys.xml",
1620
"DataProtectionKeyUri": "https://dotneteng-status-staging.vault.azure.net/keys/dotnet-status-data-protection/"

src/DotNet.Status.Web/DotNet.Status.Web/.config/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,14 @@
285285
"RcaRequestedLabels": [ "RCA Requested" ],
286286
"RcaLabel": "RCA"
287287
},
288+
"AzureDevOpsAlert": {
289+
"Organization": "dnceng",
290+
"Project": "internal",
291+
"AreaPath": "internal\\.NET Engineering Services\\First Responders",
292+
"WorkItemType": "DNCENG Task",
293+
"TitlePrefix": "",
294+
"SupplementalBodyText": ""
295+
},
288296
"AzureTableTokenStore": {
289297
"TableName": "tokens"
290298
},

0 commit comments

Comments
 (0)