Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 71d870d

Browse files
authored
Merge branch 'main' into kanan/oom-logging
2 parents b79b121 + d50fd48 commit 71d870d

File tree

26 files changed

+248
-164
lines changed

26 files changed

+248
-164
lines changed

src/ApiService/ApiService/Functions/Containers.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ public ContainersFunction(ILogger<ContainersFunction> logger, IOnefuzzContext co
1616

1717
[Function("Containers")]
1818
[Authorize(Allow.User)]
19-
public Async.Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "GET", "POST", "DELETE")] HttpRequestData req)
19+
public Async.Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "GET", "POST", "PATCH", "DELETE")] HttpRequestData req)
2020
=> req.Method switch {
2121
"GET" => Get(req),
2222
"POST" => Post(req),
2323
"DELETE" => Delete(req),
24+
"PATCH" => Patch(req),
2425
_ => throw new NotSupportedException(),
2526
};
2627

@@ -108,4 +109,21 @@ private async Async.Task<HttpResponseData> Post(HttpRequestData req) {
108109
SasUrl: sas,
109110
Metadata: post.Metadata));
110111
}
112+
113+
private async Async.Task<HttpResponseData> Patch(HttpRequestData req) {
114+
var request = await RequestHandling.ParseRequest<ContainerUpdate>(req);
115+
if (!request.IsOk) {
116+
return await _context.RequestHandling.NotOk(req, request.ErrorV, context: "container update");
117+
}
118+
119+
var toUpdate = request.OkV;
120+
_logger.LogInformation("updating {ContainerName}", toUpdate.Name);
121+
var updated = await _context.Containers.CreateOrUpdateContainerTag(toUpdate.Name, StorageType.Corpus, toUpdate.Metadata.ToDictionary(x => x.Key, x => x.Value));
122+
123+
if (!updated.IsOk) {
124+
return await _context.RequestHandling.NotOk(req, updated.ErrorV, "container update");
125+
}
126+
127+
return await RequestHandling.Ok(req, new ContainerInfoBase(toUpdate.Name, toUpdate.Metadata));
128+
}
111129
}

src/ApiService/ApiService/Functions/QueueFileChanges.cs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Text.Json.Nodes;
33
using System.Threading.Tasks;
44
using Azure.Core;
5+
using Azure.Storage.Blobs;
6+
using Azure.Storage.Blobs.Models;
57
using Microsoft.Azure.Functions.Worker;
68
using Microsoft.Extensions.Logging;
79
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
@@ -60,7 +62,11 @@ public async Async.Task Run(
6062
try {
6163
var result = await FileAdded(storageAccount, fileChangeEvent);
6264
if (!result.IsOk) {
63-
await RequeueMessage(msg, result.ErrorV.Code == ErrorCode.ADO_WORKITEM_PROCESSING_DISABLED ? TimeSpan.FromDays(1) : null);
65+
if (result.ErrorV.Code == ErrorCode.ADO_WORKITEM_PROCESSING_DISABLED) {
66+
await RequeueMessage(msg, TimeSpan.FromDays(1), incrementDequeueCount: false);
67+
} else {
68+
await RequeueMessage(msg);
69+
}
6470
}
6571
} catch (Exception e) {
6672
_log.LogError(e, "File Added failed");
@@ -83,21 +89,26 @@ private async Async.Task<OneFuzzResultVoid> FileAdded(ResourceIdentifier storage
8389

8490
_log.LogInformation("file added : {Container} - {Path}", container.String, path);
8591

92+
var account = await _storage.GetBlobServiceClientForAccount(storageAccount);
93+
var containerClient = account.GetBlobContainerClient(container.String);
94+
var containerProps = await containerClient.GetPropertiesAsync();
95+
96+
if (_context.NotificationOperations.ShouldPauseNotificationsForContainer(containerProps.Value.Metadata)) {
97+
return Error.Create(ErrorCode.ADO_WORKITEM_PROCESSING_DISABLED, $"container {container} has a metadata tag set to pause notifications processing");
98+
}
99+
86100
var (_, result) = await (
87-
ApplyRetentionPolicy(storageAccount, container, path),
101+
ApplyRetentionPolicy(containerClient, containerProps, path),
88102
_notificationOperations.NewFiles(container, path));
89103

90104
return result;
91105
}
92106

93-
private async Async.Task<bool> ApplyRetentionPolicy(ResourceIdentifier storageAccount, Container container, string path) {
107+
private async Async.Task<bool> ApplyRetentionPolicy(BlobContainerClient containerClient, BlobContainerProperties containerProps, string path) {
94108
if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableContainerRetentionPolicies)) {
95109
// default retention period can be applied to the container
96110
// if one exists, we will set the expiry date on the newly-created blob, if it doesn't already have one
97-
var account = await _storage.GetBlobServiceClientForAccount(storageAccount);
98-
var containerClient = account.GetBlobContainerClient(container.String);
99-
var containerProps = await containerClient.GetPropertiesAsync();
100-
var retentionPeriod = RetentionPolicyUtils.GetContainerRetentionPeriodFromMetadata(containerProps.Value.Metadata);
111+
var retentionPeriod = RetentionPolicyUtils.GetContainerRetentionPeriodFromMetadata(containerProps.Metadata);
101112
if (!retentionPeriod.IsOk) {
102113
_log.LogError("invalid retention period: {Error}", retentionPeriod.ErrorV);
103114
} else if (retentionPeriod.OkV is TimeSpan period) {
@@ -116,7 +127,7 @@ private async Async.Task<bool> ApplyRetentionPolicy(ResourceIdentifier storageAc
116127
return false;
117128
}
118129

119-
private async Async.Task RequeueMessage(string msg, TimeSpan? visibilityTimeout = null) {
130+
private async Async.Task RequeueMessage(string msg, TimeSpan? visibilityTimeout = null, bool incrementDequeueCount = true) {
120131
var json = JsonNode.Parse(msg);
121132

122133
// Messages that are 'manually' requeued by us as opposed to being requeued by the azure functions runtime
@@ -135,7 +146,9 @@ await _context.Queue.QueueObject(
135146
StorageType.Config)
136147
.IgnoreResult();
137148
} else {
138-
json!["data"]!["customDequeueCount"] = newCustomDequeueCount + 1;
149+
if (incrementDequeueCount) {
150+
json!["data"]!["customDequeueCount"] = newCustomDequeueCount + 1;
151+
}
139152
await _context.Queue.QueueObject(
140153
QueueFileChangesQueueName,
141154
json,

src/ApiService/ApiService/Functions/QueueJobResult.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ public async Async.Task Run([QueueTrigger("job-result", Connection = "AzureWebJo
3131

3232
var job = await _jobs.Get(task.JobId);
3333
if (job == null) {
34-
_log.LogWarning("invalid {JobId}", task.JobId);
34+
_log.LogWarning("invalid message {JobId}", task.JobId);
35+
return;
36+
}
37+
38+
if (jr.CreatedAt == null) {
39+
_log.LogWarning("invalid message, no created_at field {JobId}", task.JobId);
3540
return;
3641
}
3742

@@ -52,7 +57,7 @@ public async Async.Task Run([QueueTrigger("job-result", Connection = "AzureWebJo
5257
return;
5358
}
5459

55-
var jobResult = await _context.JobResultOperations.CreateOrUpdate(job.JobId, jobResultType, value);
60+
var jobResult = await _context.JobResultOperations.CreateOrUpdate(job.JobId, jr.TaskId, jr.MachineId, jr.CreatedAt.Value, jr.Version, jobResultType, value);
5661
if (!jobResult.IsOk) {
5762
_log.LogError("failed to create or update with job result {JobId}", job.JobId);
5863
}

src/ApiService/ApiService/OneFuzzTypes/Enums.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public enum ErrorCode {
5353
INVALID_RETENTION_PERIOD = 497,
5454
INVALID_CLI_VERSION = 498,
5555
TRANSIENT_NOTIFICATION_FAILURE = 499,
56+
57+
FAILED_CONTAINER_PROPERTIES_ACCESS = 500,
58+
FAILED_SAVING_CONTAINER_METADATA = 501,
5659
// NB: if you update this enum, also update enums.py
5760
}
5861

src/ApiService/ApiService/OneFuzzTypes/Model.cs

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,6 @@ public enum HeartbeatType {
3434
TaskAlive,
3535
}
3636

37-
[SkipRename]
38-
public enum JobResultType {
39-
NewCrashingInput,
40-
NoReproCrashingInput,
41-
NewReport,
42-
NewUniqueReport,
43-
NewRegressionReport,
44-
NewCoverage,
45-
NewCrashDump,
46-
CoverageData,
47-
RuntimeStats,
48-
}
49-
5037
public record HeartbeatData(HeartbeatType Type);
5138

5239
public record TaskHeartbeatEntry(
@@ -55,12 +42,14 @@ public record TaskHeartbeatEntry(
5542
[property: Required] Guid MachineId,
5643
HeartbeatData[] Data);
5744

58-
public record JobResultData(JobResultType Type);
45+
public record JobResultData(string Type);
5946

6047
public record TaskJobResultEntry(
6148
Guid TaskId,
6249
Guid? JobId,
6350
Guid MachineId,
51+
DateTime? CreatedAt,
52+
double Version,
6453
JobResultData Data,
6554
Dictionary<string, double> Value
6655
);
@@ -921,26 +910,24 @@ public record SecretAddress<T>(Uri Url) : ISecret<T> {
921910
public record SecretData<T>(ISecret<T> Secret) {
922911
}
923912

913+
[SkipRename]
914+
public enum JobResultType {
915+
CoverageData,
916+
RuntimeStats,
917+
}
918+
924919
public record JobResult(
925-
[PartitionKey][RowKey] Guid JobId,
920+
[PartitionKey] Guid JobId,
921+
[RowKey] string TaskIdMachineIdMetric,
922+
Guid TaskId,
923+
Guid MachineId,
924+
DateTime CreatedAt,
926925
string Project,
927926
string Name,
928-
double NewCrashingInput = 0,
929-
double NoReproCrashingInput = 0,
930-
double NewReport = 0,
931-
double NewUniqueReport = 0,
932-
double NewRegressionReport = 0,
933-
double NewCrashDump = 0,
934-
double InstructionsCovered = 0,
935-
double TotalInstructions = 0,
936-
double CoverageRate = 0,
937-
double IterationCount = 0
938-
) : EntityBase() {
939-
public JobResult(Guid JobId, string Project, string Name) : this(
940-
JobId: JobId,
941-
Project: Project,
942-
Name: Name, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) { }
943-
}
927+
string Type,
928+
double Version,
929+
Dictionary<string, double> MetricValue
930+
) : EntityBase();
944931

945932
public record JobConfig(
946933
string Project,

src/ApiService/ApiService/OneFuzzTypes/Requests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public record ContainerDelete(
128128
IDictionary<string, string>? Metadata = null
129129
) : BaseRequest;
130130

131+
public record ContainerUpdate(
132+
[property: Required] Container Name,
133+
[property: Required] IDictionary<string, string> Metadata
134+
) : BaseRequest;
135+
131136
public record NotificationCreate(
132137
[property: Required] Container Container,
133138
[property: Required] bool ReplaceExisting,

src/ApiService/ApiService/host.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"version": "2.0",
3+
"functionTimeout": "12:00:00",
34
"logging": {
45
"applicationInsights": {
56
"samplingSettings": {

src/ApiService/ApiService/onefuzzlib/Containers.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.IO;
22
using System.IO.Compression;
3+
using System.Text.Json;
34
using System.Threading.Tasks;
45
using ApiService.OneFuzzLib.Orm;
56
using Azure;
@@ -41,6 +42,8 @@ public interface IContainers {
4142
public Async.Task<OneFuzzResultVoid> DownloadAsZip(Container container, StorageType storageType, Stream stream, string? prefix = null);
4243

4344
public Async.Task DeleteAllExpiredBlobs();
45+
46+
public Async.Task<OneFuzzResultVoid> CreateOrUpdateContainerTag(Container container, StorageType storageType, Dictionary<string, string> tags);
4447
}
4548

4649
public class Containers : Orm<ContainerInformation>, IContainers {
@@ -448,4 +451,29 @@ private async Async.Task DeleteExpiredBlobsForAccount(ResourceIdentifier storage
448451
}
449452
}
450453
}
454+
455+
public async Task<OneFuzzResultVoid> CreateOrUpdateContainerTag(Container container, StorageType storageType, Dictionary<string, string> tags) {
456+
var client = await FindContainer(container, storageType);
457+
if (client is null || !await client.ExistsAsync()) {
458+
return Error.Create(ErrorCode.INVALID_CONTAINER, $"Could not find container {container} in {storageType}");
459+
}
460+
461+
var metadataRequest = await client.GetPropertiesAsync();
462+
if (metadataRequest is null || metadataRequest.GetRawResponse().IsError) {
463+
return Error.Create(ErrorCode.FAILED_CONTAINER_PROPERTIES_ACCESS, $"Could not access container properties for container: {container} in {storageType}");
464+
}
465+
466+
var metadata = metadataRequest.Value.Metadata ?? new Dictionary<string, string>();
467+
468+
foreach (var kvp in tags) {
469+
metadata[kvp.Key] = kvp.Value;
470+
}
471+
472+
var saveMetadataRequest = await client.SetMetadataAsync(metadata);
473+
if (saveMetadataRequest is null || saveMetadataRequest.GetRawResponse().IsError) {
474+
return Error.Create(ErrorCode.FAILED_SAVING_CONTAINER_METADATA, $"Could not save metadata to container: {container} in {storageType}. Metadata: {JsonSerializer.Serialize(metadata)}");
475+
}
476+
477+
return OneFuzzResultVoid.Ok;
478+
}
451479
}

0 commit comments

Comments
 (0)