Laying groundwork for TimerRepro implementation (#2168)

* Add some pre-reqs for TimerRepro

* Format and add some API to creds

* PR comment
This commit is contained in:
Teo Voinea
2022-07-15 14:33:25 -04:00
committed by GitHub
parent 3347e7e67b
commit 7f90c13724
8 changed files with 99 additions and 45 deletions

View File

@ -165,7 +165,7 @@ public static class ScalesetStateHelper {
public static class VmStateHelper {
private static readonly IReadOnlySet<VmState> _needsWork = new HashSet<VmState> { VmState.Init, VmState.Init, VmState.ExtensionsLaunch, VmState.Stopping };
private static readonly IReadOnlySet<VmState> _needsWork = new HashSet<VmState> { VmState.Init, VmState.ExtensionsLaunch, VmState.Stopping };
private static readonly IReadOnlySet<VmState> _available = new HashSet<VmState> { VmState.Init, VmState.ExtensionsLaunch, VmState.ExtensionsFailed, VmState.VmAllocationFailed, VmState.Running, };
public static IReadOnlySet<VmState> NeedsWork => _needsWork;

View File

@ -147,7 +147,12 @@ public record Proxy
bool Outdated
) : StatefulEntityBase<VmState>(State);
public record Error(ErrorCode Code, string[]? Errors = null);
public record Error(ErrorCode Code, string[]? Errors = null) {
public sealed override string ToString() {
var errorsString = Errors != null ? string.Join("", Errors) : string.Empty;
return $"Error {{ Code = {Code}, Errors = {errorsString} }}";
}
};
public record UserInfo(Guid? ApplicationId, Guid? ObjectId, String? Upn);
@ -413,7 +418,7 @@ public record Notification(
public record BlobRef(
string Account,
Container container,
string name
string Name
);
public record Report(
@ -436,7 +441,7 @@ public record Report(
string? MinimizedStackFunctionNamesSha256,
List<string>? MinimizedStackFunctionLines,
string? MinimizedStackFunctionLinesSha256
);
) : IReport;
public record NoReproReport(
string InputSha,
@ -444,7 +449,7 @@ public record NoReproReport(
string? Executable,
Guid TaskId,
Guid JobId,
int Tries,
long Tries,
string? Error
);
@ -456,7 +461,7 @@ public record CrashTestResult(
public record RegressionReport(
CrashTestResult CrashTestResult,
CrashTestResult? OriginalCrashTestResult
);
) : IReport;
public record NotificationTemplate(
AdoTemplate? AdoTemplate,
@ -471,8 +476,7 @@ public record TeamsTemplate();
public record GithubIssuesTemplate();
public record Repro(
[PartitionKey] Guid VmId,
[RowKey] Guid _,
[PartitionKey][RowKey] Guid VmId,
Guid TaskId,
ReproConfig Config,
VmState State,

View File

@ -28,6 +28,10 @@ public interface ICreds {
public Uri GetInstanceUrl();
public Async.Task<Guid> GetScalesetPrincipalId();
public Async.Task<T> QueryMicrosoftGraph<T>(HttpMethod method, string resource);
public GenericResource ParseResourceId(string resourceId);
public Async.Task<GenericResource> GetData(GenericResource resource);
}
public class Creds : ICreds {
@ -145,6 +149,17 @@ public class Creds : ICreds {
throw new GraphQueryException($"request did not succeed: HTTP {response.StatusCode} - {errorText}");
}
}
public GenericResource ParseResourceId(string resourceId) {
return ArmClient.GetGenericResource(new ResourceIdentifier(resourceId));
}
public async Async.Task<GenericResource> GetData(GenericResource resource) {
if (!resource.HasData) {
return await resource.GetAsync();
}
return resource;
}
}
class GraphQueryException : Exception {

View File

@ -19,7 +19,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
public async Async.Task NewFiles(Container container, string filename, bool failTaskOnTransientError) {
var notifications = GetNotifications(container);
var hasNotifications = await notifications.AnyAsync();
var report = await _context.Reports.GetReportOrRegression(container, filename, expectReports: hasNotifications);
var reportOrRegression = await _context.Reports.GetReportOrRegression(container, filename, expectReports: hasNotifications);
if (!hasNotifications) {
return;
@ -34,19 +34,19 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
done.Add(notification.Config);
if (notification.Config.TeamsTemplate != null) {
NotifyTeams(notification.Config.TeamsTemplate, container, filename, report);
NotifyTeams(notification.Config.TeamsTemplate, container, filename, reportOrRegression!);
}
if (report == null) {
if (reportOrRegression == null) {
continue;
}
if (notification.Config.AdoTemplate != null) {
NotifyAdo(notification.Config.AdoTemplate, container, filename, report, failTaskOnTransientError);
NotifyAdo(notification.Config.AdoTemplate, container, filename, reportOrRegression, failTaskOnTransientError);
}
if (notification.Config.GithubIssuesTemplate != null) {
GithubIssue(notification.Config.GithubIssuesTemplate, container, filename, report);
GithubIssue(notification.Config.GithubIssuesTemplate, container, filename, reportOrRegression);
}
}
@ -58,15 +58,17 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
}
}
if (report?.Report != null) {
var reportTask = await _context.TaskOperations.GetByJobIdAndTaskId(report.Report.JobId, report.Report.TaskId);
if (reportOrRegression is Report) {
var report = (reportOrRegression as Report)!;
var reportTask = await _context.TaskOperations.GetByJobIdAndTaskId(report.JobId, report.TaskId);
var crashReportedEvent = new EventCrashReported(report.Report, container, filename, reportTask?.Config);
var crashReportedEvent = new EventCrashReported(report, container, filename, reportTask?.Config);
await _context.Events.SendEvent(crashReportedEvent);
} else if (report?.RegressionReport != null) {
var reportTask = await GetRegressionReportTask(report.RegressionReport);
} else if (reportOrRegression is RegressionReport) {
var regressionReport = (reportOrRegression as RegressionReport)!;
var reportTask = await GetRegressionReportTask(regressionReport);
var regressionEvent = new EventRegressionReported(report.RegressionReport, container, filename, reportTask?.Config);
var regressionEvent = new EventRegressionReported(regressionReport, container, filename, reportTask?.Config);
await _context.Events.SendEvent(regressionEvent);
} else {
await _context.Events.SendEvent(new EventFileAdded(container, filename));
@ -96,15 +98,15 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
return null;
}
private void GithubIssue(GithubIssuesTemplate config, Container container, string filename, RegressionReportOrReport? report) {
private void GithubIssue(GithubIssuesTemplate config, Container container, string filename, IReport report) {
throw new NotImplementedException();
}
private void NotifyAdo(AdoTemplate config, Container container, string filename, RegressionReportOrReport report, bool failTaskOnTransientError) {
private void NotifyAdo(AdoTemplate config, Container container, string filename, IReport report, bool failTaskOnTransientError) {
throw new NotImplementedException();
}
private void NotifyTeams(TeamsTemplate config, Container container, string filename, RegressionReportOrReport? report) {
private void NotifyTeams(TeamsTemplate config, Container container, string filename, IReport report) {
throw new NotImplementedException();
}
}

View File

@ -4,7 +4,7 @@ using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
namespace Microsoft.OneFuzz.Service;
public interface IReports {
public Async.Task<RegressionReportOrReport?> GetReportOrRegression(Container container, string fileName, bool expectReports = false, params string[] args);
public Async.Task<IReport?> GetReportOrRegression(Container container, string fileName, bool expectReports = false, params string[] args);
}
public class Reports : IReports {
@ -15,7 +15,7 @@ public class Reports : IReports {
_containers = containers;
}
public async Async.Task<RegressionReportOrReport?> GetReportOrRegression(Container container, string fileName, bool expectReports = false, params string[] args) {
public async Async.Task<IReport?> GetReportOrRegression(Container container, string fileName, bool expectReports = false, params string[] args) {
var filePath = String.Join("/", new[] { container.ContainerName, fileName });
if (!fileName.EndsWith(".json", StringComparison.Ordinal)) {
if (expectReports) {
@ -36,26 +36,20 @@ public class Reports : IReports {
return ParseReportOrRegression(blob.ToString(), filePath, expectReports);
}
private RegressionReportOrReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false) {
try {
return new RegressionReportOrReport {
RegressionReport = JsonSerializer.Deserialize<RegressionReport>(content, EntityConverter.GetJsonSerializerOptions())
};
} catch (JsonException e) {
try {
return new RegressionReportOrReport {
Report = JsonSerializer.Deserialize<Report>(content, EntityConverter.GetJsonSerializerOptions())
};
} catch (JsonException e2) {
if (expectReports) {
_log.Error($"unable to parse report ({filePath}) as a report or regression. regression error: {e.Message} report error: {e2.Message}");
}
private IReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false) {
var regressionReport = JsonSerializer.Deserialize<RegressionReport>(content, EntityConverter.GetJsonSerializerOptions());
if (regressionReport == null || regressionReport.CrashTestResult == null) {
var report = JsonSerializer.Deserialize<Report>(content, EntityConverter.GetJsonSerializerOptions());
if (expectReports && report == null) {
_log.Error($"unable to parse report ({filePath}) as a report or regression");
return null;
}
return report;
}
return regressionReport;
}
private RegressionReportOrReport? ParseReportOrRegression(IEnumerable<byte> content, string? filePath, bool expectReports = false) {
private IReport? ParseReportOrRegression(IEnumerable<byte> content, string? filePath, bool expectReports = false) {
try {
var str = System.Text.Encoding.UTF8.GetString(content.ToArray());
return ParseReportOrRegression(str, filePath, expectReports);
@ -68,7 +62,4 @@ public class Reports : IReports {
}
}
public class RegressionReportOrReport {
public RegressionReport? RegressionReport { get; set; }
public Report? Report { get; set; }
}
public interface IReport { };

View File

@ -56,4 +56,12 @@ class TestCreds : ICreds {
public Task<T> QueryMicrosoftGraph<T>(HttpMethod method, string resource) {
throw new NotImplementedException();
}
public GenericResource ParseResourceId(string resourceId) {
throw new NotImplementedException();
}
public Task<GenericResource> GetData(GenericResource resource) {
throw new NotImplementedException();
}
}

View File

@ -300,6 +300,41 @@ namespace Tests {
);
}
public static Gen<NoReproReport> NoReproReport() {
return Arb.Generate<Tuple<string, BlobRef?, string?, Guid, int>>().Select(
arg =>
new NoReproReport(
arg.Item1,
arg.Item2,
arg.Item3,
arg.Item4,
arg.Item4,
arg.Item5,
arg.Item3
)
);
}
public static Gen<CrashTestResult> CrashTestResult() {
return Arb.Generate<Tuple<Report, NoReproReport>>().Select(
arg =>
new CrashTestResult(
arg.Item1,
arg.Item2
)
);
}
public static Gen<RegressionReport> RegressionReport() {
return Arb.Generate<Tuple<CrashTestResult, CrashTestResult?>>().Select(
arg =>
new RegressionReport(
arg.Item1,
arg.Item2
)
);
}
public static Gen<Container> Container() {
return Arb.Generate<Tuple<NonNull<string>>>().Select(
arg => new Container(string.Join("", arg.Item1.Get.Where(c => char.IsLetterOrDigit(c) || c == '-'))!)
@ -801,7 +836,7 @@ namespace Tests {
[Property]
public bool RegressionReportOrReport(RegressionReportOrReport e) {
public bool RegressionReport(RegressionReport e) {
return Test(e);
}

View File

@ -99,7 +99,6 @@ public class TimerReproTests {
return new Repro(
Guid.NewGuid(),
Guid.Empty,
Guid.Empty,
new ReproConfig(
new Container(String.Empty),
String.Empty,