This repository has been archived by the owner on Nov 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into Release-8.7.0
- Loading branch information
Showing
33 changed files
with
671 additions
and
40 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System.Text.Json; | ||
using Microsoft.Azure.Functions.Worker; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; | ||
namespace Microsoft.OneFuzz.Service.Functions; | ||
|
||
|
||
public class QueueJobResult { | ||
private readonly ILogger _log; | ||
private readonly IOnefuzzContext _context; | ||
|
||
public QueueJobResult(ILogger<QueueJobResult> logTracer, IOnefuzzContext context) { | ||
_log = logTracer; | ||
_context = context; | ||
} | ||
|
||
[Function("QueueJobResult")] | ||
public async Async.Task Run([QueueTrigger("job-result", Connection = "AzureWebJobsStorage")] string msg) { | ||
|
||
var _tasks = _context.TaskOperations; | ||
var _jobs = _context.JobOperations; | ||
|
||
_log.LogInformation("job result: {msg}", msg); | ||
var jr = JsonSerializer.Deserialize<TaskJobResultEntry>(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}"); | ||
|
||
var task = await _tasks.GetByTaskId(jr.TaskId); | ||
if (task == null) { | ||
_log.LogWarning("invalid {TaskId}", jr.TaskId); | ||
return; | ||
} | ||
|
||
var job = await _jobs.Get(task.JobId); | ||
if (job == null) { | ||
_log.LogWarning("invalid {JobId}", task.JobId); | ||
return; | ||
} | ||
|
||
JobResultData? data = jr.Data; | ||
if (data == null) { | ||
_log.LogWarning($"job result data is empty, throwing out: {jr}"); | ||
return; | ||
} | ||
|
||
var jobResultType = data.Type; | ||
_log.LogInformation($"job result data type: {jobResultType}"); | ||
|
||
Dictionary<string, double> value; | ||
if (jr.Value.Count > 0) { | ||
value = jr.Value; | ||
} else { | ||
_log.LogWarning($"job result data is empty, throwing out: {jr}"); | ||
return; | ||
} | ||
|
||
var jobResult = await _context.JobResultOperations.CreateOrUpdate(job.JobId, jobResultType, value); | ||
if (!jobResult.IsOk) { | ||
_log.LogError("failed to create or update with job result {JobId}", job.JobId); | ||
} | ||
} | ||
} |
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
125 changes: 125 additions & 0 deletions
125
src/ApiService/ApiService/onefuzzlib/JobResultOperations.cs
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,125 @@ | ||
using ApiService.OneFuzzLib.Orm; | ||
using Microsoft.Extensions.Logging; | ||
using Polly; | ||
namespace Microsoft.OneFuzz.Service; | ||
|
||
public interface IJobResultOperations : IOrm<JobResult> { | ||
|
||
Async.Task<JobResult?> GetJobResult(Guid jobId); | ||
Async.Task<OneFuzzResultVoid> CreateOrUpdate(Guid jobId, JobResultType resultType, Dictionary<string, double> resultValue); | ||
|
||
} | ||
public class JobResultOperations : Orm<JobResult>, IJobResultOperations { | ||
|
||
public JobResultOperations(ILogger<JobResultOperations> log, IOnefuzzContext context) | ||
: base(log, context) { | ||
} | ||
|
||
public async Async.Task<JobResult?> GetJobResult(Guid jobId) { | ||
return await SearchByPartitionKeys(new[] { jobId.ToString() }).SingleOrDefaultAsync(); | ||
} | ||
|
||
private JobResult UpdateResult(JobResult result, JobResultType type, Dictionary<string, double> resultValue) { | ||
|
||
var newResult = result; | ||
double newValue; | ||
switch (type) { | ||
case JobResultType.NewCrashingInput: | ||
newValue = result.NewCrashingInput + resultValue["count"]; | ||
newResult = result with { NewCrashingInput = newValue }; | ||
break; | ||
case JobResultType.NewReport: | ||
newValue = result.NewReport + resultValue["count"]; | ||
newResult = result with { NewReport = newValue }; | ||
break; | ||
case JobResultType.NewUniqueReport: | ||
newValue = result.NewUniqueReport + resultValue["count"]; | ||
newResult = result with { NewUniqueReport = newValue }; | ||
break; | ||
case JobResultType.NewRegressionReport: | ||
newValue = result.NewRegressionReport + resultValue["count"]; | ||
newResult = result with { NewRegressionReport = newValue }; | ||
break; | ||
case JobResultType.NewCrashDump: | ||
newValue = result.NewCrashDump + resultValue["count"]; | ||
newResult = result with { NewCrashDump = newValue }; | ||
break; | ||
case JobResultType.CoverageData: | ||
double newCovered = resultValue["covered"]; | ||
double newTotalCovered = resultValue["features"]; | ||
double newCoverageRate = resultValue["rate"]; | ||
newResult = result with { InstructionsCovered = newCovered, TotalInstructions = newTotalCovered, CoverageRate = newCoverageRate }; | ||
break; | ||
case JobResultType.RuntimeStats: | ||
double newTotalIterations = resultValue["total_count"]; | ||
newResult = result with { IterationCount = newTotalIterations }; | ||
break; | ||
default: | ||
_logTracer.LogWarning($"Invalid Field {type}."); | ||
break; | ||
} | ||
_logTracer.LogInformation($"Attempting to log new result: {newResult}"); | ||
return newResult; | ||
} | ||
|
||
private async Async.Task<bool> TryUpdate(Job job, JobResultType resultType, Dictionary<string, double> resultValue) { | ||
var jobId = job.JobId; | ||
|
||
var jobResult = await GetJobResult(jobId); | ||
|
||
if (jobResult == null) { | ||
_logTracer.LogInformation("Creating new JobResult for Job {JobId}", jobId); | ||
|
||
var entry = new JobResult(JobId: jobId, Project: job.Config.Project, Name: job.Config.Name); | ||
|
||
jobResult = UpdateResult(entry, resultType, resultValue); | ||
|
||
var r = await Insert(jobResult); | ||
if (!r.IsOk) { | ||
_logTracer.AddHttpStatus(r.ErrorV); | ||
_logTracer.LogError("failed to insert job result {JobId}", jobResult.JobId); | ||
throw new InvalidOperationException($"failed to insert job result {jobResult.JobId}"); | ||
} | ||
_logTracer.LogInformation("created job result {JobId}", jobResult.JobId); | ||
} else { | ||
_logTracer.LogInformation("Updating existing JobResult entry for Job {JobId}", jobId); | ||
|
||
jobResult = UpdateResult(jobResult, resultType, resultValue); | ||
|
||
var r = await Update(jobResult); | ||
if (!r.IsOk) { | ||
_logTracer.AddHttpStatus(r.ErrorV); | ||
_logTracer.LogError("failed to update job result {JobId}", jobResult.JobId); | ||
throw new InvalidOperationException($"failed to insert job result {jobResult.JobId}"); | ||
} | ||
_logTracer.LogInformation("updated job result {JobId}", jobResult.JobId); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public async Async.Task<OneFuzzResultVoid> CreateOrUpdate(Guid jobId, JobResultType resultType, Dictionary<string, double> resultValue) { | ||
|
||
var job = await _context.JobOperations.Get(jobId); | ||
if (job == null) { | ||
return OneFuzzResultVoid.Error(ErrorCode.INVALID_REQUEST, "invalid job"); | ||
} | ||
|
||
var success = false; | ||
try { | ||
_logTracer.LogInformation("attempt to update job result {JobId}", job.JobId); | ||
var policy = Policy.Handle<InvalidOperationException>().WaitAndRetryAsync(50, _ => new TimeSpan(0, 0, 5)); | ||
await policy.ExecuteAsync(async () => { | ||
success = await TryUpdate(job, resultType, resultValue); | ||
_logTracer.LogInformation("attempt {success}", success); | ||
}); | ||
return OneFuzzResultVoid.Ok; | ||
} catch (Exception e) { | ||
return OneFuzzResultVoid.Error(ErrorCode.UNABLE_TO_UPDATE, new string[] { | ||
$"Unexpected failure when attempting to update job result for {job.JobId}", | ||
$"Exception: {e}" | ||
}); | ||
} | ||
} | ||
} | ||
|
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Oops, something went wrong.