[C# Port] Adding new Proxy Update Queue Function. (#1757)

* Adding QueueProxyUpdate.

* Setting to serializer.

* Updates.

* Updating with new ORM model and [model]Operation.

* Fixing return type.

* Working on changes.

* Tested and ready for review.

* Formatting.

* Removing test code.

* Update src/ApiService/Tests/OrmTest.cs

Co-authored-by: Cheick Keita <chkeita@microsoft.com>

* Fixing tests.

* Fixing tests again.

* Asserting null in tests.

* Adding null param.

* Removing whitespace.

* syntax error.

Co-authored-by: Cheick Keita <chkeita@microsoft.com>
This commit is contained in:
Noah McGregor Harper
2022-04-11 13:32:43 -07:00
committed by GitHub
parent 5aceb25843
commit 75039a96eb
7 changed files with 141 additions and 2 deletions

View File

@ -26,6 +26,16 @@
INVALID_CONFIGURATION = 473, INVALID_CONFIGURATION = 473,
} }
public enum VmState
{
Init,
ExtensionsLaunched,
ExtensionsFailed,
VmAllocationFailed,
Running,
Stopping,
Stopped
}
public enum WebhookMessageState public enum WebhookMessageState
{ {

View File

@ -1,5 +1,6 @@
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
using System; using System;
using System.Collections.Generic;
using PoolName = System.String; using PoolName = System.String;
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
@ -14,6 +15,12 @@ namespace Microsoft.OneFuzz.Service;
/// the "partion key" and "row key" are identified by the [PartitionKey] and [RowKey] attributes /// the "partion key" and "row key" are identified by the [PartitionKey] and [RowKey] attributes
/// Guids are mapped to string in the db /// Guids are mapped to string in the db
public record Authentication
(
string Password,
string PublicKey,
string PrivateKey
);
[SkipRename] [SkipRename]
public enum HeartbeatType public enum HeartbeatType
@ -70,6 +77,13 @@ public enum NodeState
Halt, Halt,
} }
public record ProxyHeartbeat
(
string Region,
Guid ProxyId,
List<ProxyForward> Forwards,
DateTimeOffset TimeStamp
);
public partial record Node public partial record Node
( (
@ -87,6 +101,39 @@ public partial record Node
) : EntityBase(); ) : EntityBase();
public partial record ProxyForward
(
[PartitionKey] string Region,
[RowKey] int DstPort,
int SrcPort,
string DstIp
) : EntityBase();
public partial record ProxyConfig
(
Uri Url,
string Notification,
string Region,
Guid? ProxyId,
List<ProxyForward> Forwards,
string InstanceTelemetryKey,
string MicrosoftTelemetryKey
);
public partial record Proxy
(
[PartitionKey] string Region,
[RowKey] Guid ProxyId,
DateTimeOffset? CreatedTimestamp,
VmState State,
Authentication Auth,
string? Ip,
Error? Error,
string Version,
ProxyHeartbeat? heartbeat
) : EntityBase();
public record Error(ErrorCode Code, string[]? Errors = null); public record Error(ErrorCode Code, string[]? Errors = null);
public record UserInfo(Guid? ApplicationId, Guid? ObjectId, String? Upn); public record UserInfo(Guid? ApplicationId, Guid? ObjectId, String? Upn);

View File

@ -42,6 +42,7 @@ public class Program
.AddSingleton<IQueue, Queue>() .AddSingleton<IQueue, Queue>()
.AddSingleton<ICreds>(_ => new Creds()) .AddSingleton<ICreds>(_ => new Creds())
.AddSingleton<IStorage, Storage>() .AddSingleton<IStorage, Storage>()
.AddSingleton<IProxyOperations, ProxyOperations>()
) )
.Build(); .Build();

View File

@ -0,0 +1,42 @@
using System;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
namespace Microsoft.OneFuzz.Service;
public class QueueProxyHearbeat
{
private readonly ILogger _logger;
private readonly IProxyOperations _proxy;
public QueueProxyHearbeat(ILoggerFactory loggerFactory, IProxyOperations proxy)
{
_logger = loggerFactory.CreateLogger<QueueProxyHearbeat>();
_proxy = proxy;
}
[Function("QueueProxyHearbeat")]
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string msg)
{
_logger.LogInformation($"heartbeat: {msg}");
var hb = JsonSerializer.Deserialize<ProxyHeartbeat>(msg, EntityConverter.GetJsonSerializerOptions()).EnsureNotNull($"wrong data {msg}"); ;
var newHb = hb with { TimeStamp = DateTimeOffset.UtcNow };
var proxy = await _proxy.GetByProxyId(newHb.ProxyId);
if (proxy == null)
{
_logger.LogWarning($"invalid proxy id: {newHb.ProxyId}");
return;
}
var newProxy = proxy with { heartbeat = newHb };
await _proxy.Replace(newProxy);
}
}

View File

@ -0,0 +1,30 @@
using ApiService.OneFuzzLib.Orm;
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.OneFuzz.Service;
public interface IProxyOperations : IOrm<Proxy>
{
Task<Proxy?> GetByProxyId(Guid proxyId);
}
public class ProxyOperations : Orm<Proxy>, IProxyOperations
{
private readonly ILogger _logger;
public ProxyOperations(ILoggerFactory loggerFactory, IStorage storage)
: base(storage)
{
_logger = loggerFactory.CreateLogger<QueueProxyHearbeat>();
}
public async Task<Proxy?> GetByProxyId(Guid proxyId)
{
var data = QueryAsync(filter: $"RowKey eq '{proxyId}'");
return await data.FirstOrDefaultAsync();
}
}

View File

@ -245,6 +245,10 @@ public class EntityConverter
else else
{ {
var value = entity.GetString(fieldName); var value = entity.GetString(fieldName);
if (value == null)
{
return null;
}
return JsonSerializer.Deserialize(value, ef.type, options: _options); ; return JsonSerializer.Deserialize(value, ef.type, options: _options); ;
} }
} }

View File

@ -39,7 +39,8 @@ namespace Tests
TestEnum TheEnum, TestEnum TheEnum,
TestFlagEnum TheFlag, TestFlagEnum TheFlag,
[property: JsonPropertyName("a__special__name")] string Renamed, [property: JsonPropertyName("a__special__name")] string Renamed,
TestObject TheObject TestObject TheObject,
TestObject? TestNull
) : EntityBase(); ) : EntityBase();
@ -60,7 +61,8 @@ namespace Tests
TheName = "testobject", TheName = "testobject",
TheEnum = TestEnum.TheTwo, TheEnum = TestEnum.TheTwo,
TheFlag = TestFlagEnum.FlagOne | TestFlagEnum.FlagTwo TheFlag = TestFlagEnum.FlagOne | TestFlagEnum.FlagTwo
}); },
null);
var tableEntity = converter.ToTableEntity(entity1); var tableEntity = converter.ToTableEntity(entity1);
Assert.NotNull(tableEntity); Assert.NotNull(tableEntity);
@ -97,6 +99,7 @@ namespace Tests
{ "the_flag", "flag_one,flag_two"}, { "the_flag", "flag_one,flag_two"},
{ "a__special__name", "renamed"}, { "a__special__name", "renamed"},
{ "the_object", "{\"the_name\": \"testName\", \"the_enum\": \"the_one\", \"the_flag\": \"flag_one,flag_two\"}"}, { "the_object", "{\"the_name\": \"testName\", \"the_enum\": \"the_one\", \"the_flag\": \"flag_one,flag_two\"}"},
{ "test_null", null},
}; };
var entity1 = converter.ToRecord<Entity1>(tableEntity); var entity1 = converter.ToRecord<Entity1>(tableEntity);
@ -109,6 +112,8 @@ namespace Tests
Assert.Equal(tableEntity.GetDouble("the_float"), entity1.TheFloat); Assert.Equal(tableEntity.GetDouble("the_float"), entity1.TheFloat);
Assert.Equal(TestEnum.TheTwo, entity1.TheEnum); Assert.Equal(TestEnum.TheTwo, entity1.TheEnum);
Assert.Equal(tableEntity.GetString("a__special__name"), entity1.Renamed); Assert.Equal(tableEntity.GetString("a__special__name"), entity1.Renamed);
Assert.Null(tableEntity.GetString("test_null"));
Assert.Null(entity1.TestNull);
Assert.Equal("testName", entity1.TheObject.TheName); Assert.Equal("testName", entity1.TheObject.TheName);
Assert.Equal(TestEnum.TheOne, entity1.TheObject.TheEnum); Assert.Equal(TestEnum.TheOne, entity1.TheObject.TheEnum);