migrate timer_proxy part 2 (#1836)

This commit is contained in:
Cheick Keita
2022-04-25 10:14:15 -07:00
committed by GitHub
parent 66796148c5
commit 3a93de4801
20 changed files with 305 additions and 66 deletions

View File

@ -8,6 +8,7 @@
<WarningLevel>5</WarningLevel> <WarningLevel>5</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Faithlife.Utility" Version="0.12.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.0" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.EventGrid" Version="2.1.0" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.EventGrid" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" /> <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />

View File

@ -1,4 +1,4 @@
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using PoolName = System.String; using PoolName = System.String;

View File

@ -95,7 +95,7 @@ public record ProxyHeartbeat
DateTimeOffset TimeStamp DateTimeOffset TimeStamp
); );
public partial record Node public record Node
( (
DateTimeOffset? InitializedAt, DateTimeOffset? InitializedAt,
[PartitionKey] PoolName PoolName, [PartitionKey] PoolName PoolName,
@ -111,27 +111,40 @@ public partial record Node
) : StatefulEntityBase<NodeState>(State); ) : StatefulEntityBase<NodeState>(State);
public partial record ProxyForward public record Forward
(
int SrcPort,
int DstPort,
string DstIp
);
public record ProxyForward
( (
[PartitionKey] Region Region, [PartitionKey] Region Region,
int Port,
Guid ScalesetId,
Guid MachineId,
Guid? ProxyId,
[RowKey] int DstPort, [RowKey] int DstPort,
int SrcPort, string DstIp,
string DstIp DateTimeOffset EndTime
) : EntityBase(); ) : EntityBase();
public partial record ProxyConfig public record ProxyConfig
( (
Uri Url, Uri Url,
string Notification, Uri Notification,
Region Region, Region Region,
Guid? ProxyId, Guid? ProxyId,
List<ProxyForward> Forwards, List<Forward> Forwards,
string InstanceTelemetryKey, string InstanceTelemetryKey,
string MicrosoftTelemetryKey string MicrosoftTelemetryKey,
Guid InstanceId
); );
public partial record Proxy public record Proxy
( (
[PartitionKey] Region Region, [PartitionKey] Region Region,
[RowKey] Guid ProxyId, [RowKey] Guid ProxyId,

View File

@ -1,4 +1,4 @@
// to avoid collision with Task in model.cs // to avoid collision with Task in model.cs
global using Async = System.Threading.Tasks; global using Async = System.Threading.Tasks;
global using System; global using System;

View File

@ -36,6 +36,8 @@ public interface IServiceConfig
public string? OneFuzzResourceGroup { get; } public string? OneFuzzResourceGroup { get; }
public string? OneFuzzTelemetry { get; } public string? OneFuzzTelemetry { get; }
public string OnefuzzVersion { get; }
} }
public class ServiceConfiguration : IServiceConfig public class ServiceConfiguration : IServiceConfig
@ -79,4 +81,5 @@ public class ServiceConfiguration : IServiceConfig
public string? OneFuzzOwner { get => Environment.GetEnvironmentVariable("ONEFUZZ_OWNER"); } public string? OneFuzzOwner { get => Environment.GetEnvironmentVariable("ONEFUZZ_OWNER"); }
public string? OneFuzzResourceGroup { get => Environment.GetEnvironmentVariable("ONEFUZZ_RESOURCE_GROUP"); } public string? OneFuzzResourceGroup { get => Environment.GetEnvironmentVariable("ONEFUZZ_RESOURCE_GROUP"); }
public string? OneFuzzTelemetry { get => Environment.GetEnvironmentVariable("ONEFUZZ_TELEMETRY"); } public string? OneFuzzTelemetry { get => Environment.GetEnvironmentVariable("ONEFUZZ_TELEMETRY"); }
public string OnefuzzVersion { get => Environment.GetEnvironmentVariable("ONEFUZZ_VERSION") ?? "0.0.0"; }
} }

View File

@ -0,0 +1,16 @@
namespace Microsoft.OneFuzz.Service;
using System.Security.Cryptography;
public class Auth
{
public static Authentication BuildAuth()
{
var rsa = RSA.Create(2048);
string header = "-----BEGIN RSA PRIVATE KEY-----";
string footer = "-----END RSA PRIVATE KEY-----";
var privateKey = $"{header}\n{Convert.ToBase64String(rsa.ExportRSAPrivateKey())}\n{footer}";
var publiceKey = $"{header}\n{Convert.ToBase64String(rsa.ExportRSAPublicKey())}\n{footer}";
return new Authentication(Guid.NewGuid().ToString(), publiceKey, privateKey);
}
}

View File

@ -3,17 +3,20 @@ using Azure.ResourceManager;
using Azure.Storage.Blobs; using Azure.Storage.Blobs;
using Azure.Storage; using Azure.Storage;
using Azure; using Azure;
using Azure.Storage.Sas;
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
public interface IContainers public interface IContainers
{ {
public Task<IEnumerable<byte>?> GetBlob(Container container, string name, StorageType storageType); public Task<BinaryData?> GetBlob(Container container, string name, StorageType storageType);
public Async.Task<BlobContainerClient?> FindContainer(Container container, StorageType storageType); public Async.Task<BlobContainerClient?> FindContainer(Container container, StorageType storageType);
public Uri GetFileSasUrl(Container container, string name, StorageType storageType, bool read = false, bool add = false, bool create = false, bool write = false, bool delete = false, bool delete_previous_version = false, bool tag = false, int days = 30, int hours = 0, int minutes = 0); public Async.Task<Uri?> GetFileSasUrl(Container container, string name, StorageType storageType, BlobSasPermissions permissions, TimeSpan? duration = null);
Async.Task saveBlob(Container container, string v1, string v2, StorageType config);
Task<Guid> GetInstanceId();
} }
public class Containers : IContainers public class Containers : IContainers
@ -29,7 +32,7 @@ public class Containers : IContainers
_creds = creds; _creds = creds;
_armClient = creds.ArmClient; _armClient = creds.ArmClient;
} }
public async Task<IEnumerable<byte>?> GetBlob(Container container, string name, StorageType storageType) public async Task<BinaryData?> GetBlob(Container container, string name, StorageType storageType)
{ {
var client = await FindContainer(container, storageType); var client = await FindContainer(container, storageType);
@ -41,7 +44,7 @@ public class Containers : IContainers
try try
{ {
return (await client.GetBlobClient(name).DownloadContentAsync()) return (await client.GetBlobClient(name).DownloadContentAsync())
.Value.Content.ToArray(); .Value.Content;
} }
catch (RequestFailedException) catch (RequestFailedException)
{ {
@ -85,9 +88,59 @@ public class Containers : IContainers
return new Uri($"https://{accountName}.blob.core.windows.net/"); return new Uri($"https://{accountName}.blob.core.windows.net/");
} }
public Uri GetFileSasUrl(Container container, string name, StorageType storageType, bool read = false, bool add = false, bool create = false, bool write = false, bool delete = false, bool delete_previous_version = false, bool tag = false, int days = 30, int hours = 0, int minutes = 0) public async Async.Task<Uri?> GetFileSasUrl(Container container, string name, StorageType storageType, BlobSasPermissions permissions, TimeSpan? duration = null)
{ {
throw new NotImplementedException(); var client = await FindContainer(container, storageType) ?? throw new Exception($"unable to find container: {container.ContainerName} - {storageType}");
var (accountName, accountKey) = _storage.GetStorageAccountNameAndKey(client.AccountName);
var (startTime, endTime) = SasTimeWindow(duration ?? TimeSpan.FromDays(30));
var sasBuilder = new BlobSasBuilder(permissions, endTime)
{
StartsOn = startTime,
BlobContainerName = container.ContainerName,
BlobName = name
};
var sasUrl = client.GetBlobClient(name).GenerateSasUri(sasBuilder);
return sasUrl;
}
public (DateTimeOffset, DateTimeOffset) SasTimeWindow(TimeSpan timeSpan)
{
// SAS URLs are valid 6 hours earlier, primarily to work around dev
// workstations having out-of-sync time. Additionally, SAS URLs are stopped
// 15 minutes later than requested based on "Be careful with SAS start time"
// guidance.
// Ref: https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview
var SAS_START_TIME_DELTA = TimeSpan.FromHours(6);
var SAS_END_TIME_DELTA = TimeSpan.FromMinutes(6);
// SAS_START_TIME_DELTA = datetime.timedelta(hours = 6)
//SAS_END_TIME_DELTA = datetime.timedelta(minutes = 15)
var now = DateTimeOffset.UtcNow;
var start = now - SAS_START_TIME_DELTA;
var expiry = now + timeSpan + SAS_END_TIME_DELTA;
return (start, expiry);
}
public async System.Threading.Tasks.Task saveBlob(Container container, string name, string data, StorageType storageType)
{
var client = await FindContainer(container, storageType) ?? throw new Exception($"unable to find container: {container.ContainerName} - {storageType}");
await client.UploadBlobAsync(name, new BinaryData(data));
}
public async Async.Task<Guid> GetInstanceId()
{
var blob = await GetBlob(new Container("base-config"), "instance_id", StorageType.Config);
if (blob == null)
{
throw new System.Exception("Blob Not Found");
}
return System.Guid.Parse(blob.ToString());
} }
} }

View File

@ -15,12 +15,11 @@ public class ConfigOperations : Orm<InstanceConfig>, IConfigOperations
{ {
private readonly IEvents _events; private readonly IEvents _events;
private readonly ILogTracer _log; private readonly ILogTracer _log;
private readonly IServiceConfig _config;
public ConfigOperations(IStorage storage, IEvents events, ILogTracer log, IServiceConfig config) : base(storage, log, config) public ConfigOperations(IStorage storage, IEvents events, ILogTracer log, IServiceConfig config) : base(storage, log, config)
{ {
_events = events; _events = events;
_log = log; _log = log;
_config = config;
} }
public async Task<InstanceConfig> Fetch() public async Task<InstanceConfig> Fetch()

View File

@ -25,12 +25,6 @@ public partial class TimerProxy
_subnet = subnet; _subnet = subnet;
} }
private static Guid GenerateGuidv5(Guid nameSpace, string name)
{
throw new NotImplementedException();
}
public static async Async.Task<Network> Create(string region, ICreds creds, IConfigOperations configOperations, ISubnet subnet) public static async Async.Task<Network> Create(string region, ICreds creds, IConfigOperations configOperations, ISubnet subnet)
{ {
var group = creds.GetBaseResourceGroup(); var group = creds.GetBaseResourceGroup();
@ -50,7 +44,7 @@ public partial class TimerProxy
} }
else else
{ {
var networkId = GenerateGuidv5(NETWORK_GUID_NAMESPACE, string.Join("|", networkConfig.AddressSpace, networkConfig.Subnet)); var networkId = Faithlife.Utility.GuidUtility.Create(NETWORK_GUID_NAMESPACE, string.Join("|", networkConfig.AddressSpace, networkConfig.Subnet), 5);
name = $"{region}-{networkId}"; name = $"{region}-{networkId}";
} }

View File

@ -1,5 +1,6 @@
using System.Text.Json; using System.Text.Json;
using ApiService.OneFuzzLib.Orm; using ApiService.OneFuzzLib.Orm;
using Azure.Storage.Sas;
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
@ -10,7 +11,6 @@ public interface INotificationOperations
public class NotificationOperations : Orm<Notification>, INotificationOperations public class NotificationOperations : Orm<Notification>, INotificationOperations
{ {
private ILogTracer _log;
private IReports _reports; private IReports _reports;
private ITaskOperations _taskOperations; private ITaskOperations _taskOperations;
@ -23,7 +23,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
public NotificationOperations(ILogTracer log, IStorage storage, IReports reports, ITaskOperations taskOperations, IContainers containers, IQueue queue, IEvents events, IServiceConfig config) public NotificationOperations(ILogTracer log, IStorage storage, IReports reports, ITaskOperations taskOperations, IContainers containers, IQueue queue, IEvents events, IServiceConfig config)
: base(storage, log, config) : base(storage, log, config)
{ {
_log = log;
_reports = reports; _reports = reports;
_taskOperations = taskOperations; _taskOperations = taskOperations;
_containers = containers; _containers = containers;
@ -76,9 +76,9 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
{ {
if (containers.Contains(container.ContainerName)) if (containers.Contains(container.ContainerName))
{ {
_log.Info($"queuing input {container.ContainerName} {filename} {task.TaskId}"); _logTracer.Info($"queuing input {container.ContainerName} {filename} {task.TaskId}");
var url = _containers.GetFileSasUrl(container, filename, StorageType.Corpus, read: true, delete: true); var url = _containers.GetFileSasUrl(container, filename, StorageType.Corpus, BlobSasPermissions.Read | BlobSasPermissions.Delete);
await _queue.SendMessage(task.TaskId.ToString(), System.Text.Encoding.UTF8.GetBytes(url.ToString()), StorageType.Corpus); await _queue.SendMessage(task.TaskId.ToString(), System.Text.Encoding.UTF8.GetBytes(url?.ToString() ?? ""), StorageType.Corpus);
} }
} }
@ -125,7 +125,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
return await _taskOperations.GetByJobIdAndTaskId(report.CrashTestResult.NoReproReport.JobId, report.CrashTestResult.NoReproReport.TaskId); return await _taskOperations.GetByJobIdAndTaskId(report.CrashTestResult.NoReproReport.JobId, report.CrashTestResult.NoReproReport.TaskId);
} }
_log.Error($"unable to find crash_report or no repro entry for report: {JsonSerializer.Serialize(report)}"); _logTracer.Error($"unable to find crash_report or no repro entry for report: {JsonSerializer.Serialize(report)}");
return null; return null;
} }

View File

@ -72,8 +72,10 @@ namespace Microsoft.OneFuzz.Service
return !active_regions.Contains(nsg_region) && nsg_region == nsg_name; return !active_regions.Contains(nsg_region) && nsg_region == nsg_name;
} }
// Returns True if deletion completed (thus resource not found) or successfully started. /// <summary>
// Returns False if failed to start deletion. /// Returns True if deletion completed (thus resource not found) or successfully started.
/// Returns False if failed to start deletion.
/// </summary>
public async Async.Task<bool> StartDeleteNsg(string name) public async Async.Task<bool> StartDeleteNsg(string name)
{ {
_logTracer.Info($"deleting nsg: {name}"); _logTracer.Info($"deleting nsg: {name}");

View File

@ -0,0 +1,34 @@
using ApiService.OneFuzzLib.Orm;
namespace Microsoft.OneFuzz.Service;
public interface IProxyForwardOperations : IOrm<ProxyForward>
{
IAsyncEnumerable<ProxyForward> SearchForward(Guid? scalesetId = null, string? region = null, Guid? machineId = null, Guid? proxyId = null, int? dstPort = null);
}
public class ProxyForwardOperations : Orm<ProxyForward>, IProxyForwardOperations
{
public ProxyForwardOperations(IStorage storage, ILogTracer logTracer, IServiceConfig config) : base(storage, logTracer, config)
{
}
public IAsyncEnumerable<ProxyForward> SearchForward(Guid? scalesetId = null, string? region = null, Guid? machineId = null, Guid? proxyId = null, int? dstPort = null)
{
var conditions =
new[] {
scalesetId != null ? $"scaleset_id eq '{scalesetId}'" : null,
region != null ? $"region eq '{region}'" : null ,
machineId != null ? $"machine_id eq '{machineId}'" : null ,
proxyId != null ? $"proxy_id eq '{proxyId}'" : null ,
dstPort != null ? $"dsp_port eq {dstPort }" : null ,
}.Where(x => x != null);
var filter = string.Join(" and ", conditions);
return QueryAsync(filter);
}
}

View File

@ -1,4 +1,5 @@
using ApiService.OneFuzzLib.Orm; using ApiService.OneFuzzLib.Orm;
using Azure.Storage.Sas;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
@ -11,19 +12,27 @@ public interface IProxyOperations : IStatefulOrm<Proxy, VmState>
bool IsAlive(Proxy proxy); bool IsAlive(Proxy proxy);
System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy); System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy);
bool IsOutdated(Proxy proxy); bool IsOutdated(Proxy proxy);
System.Threading.Tasks.Task GetOrCreate(string region); System.Threading.Tasks.Task<Proxy?> GetOrCreate(string region);
} }
public class ProxyOperations : StatefulOrm<Proxy, VmState>, IProxyOperations public class ProxyOperations : StatefulOrm<Proxy, VmState>, IProxyOperations
{ {
private readonly ILogTracer _log;
private readonly IEvents _events; private readonly IEvents _events;
private readonly IProxyForwardOperations _proxyForwardOperations;
private readonly IContainers _containers;
private readonly IQueue _queue;
private readonly ICreds _creds;
public ProxyOperations(ILogTracer log, IStorage storage, IEvents events, IServiceConfig config) static TimeSpan PROXY_LIFESPAN = TimeSpan.FromDays(7);
: base(storage, log, config)
public ProxyOperations(ILogTracer log, IStorage storage, IEvents events, IProxyForwardOperations proxyForwardOperations, IContainers containers, IQueue queue, ICreds creds, IServiceConfig config)
: base(storage, log.WithTag("Component", "scaleset-proxy"), config)
{ {
_log = log;
_events = events; _events = events;
_proxyForwardOperations = proxyForwardOperations;
_containers = containers;
_queue = queue;
_creds = creds;
} }
public async Task<Proxy?> GetByProxyId(Guid proxyId) public async Task<Proxy?> GetByProxyId(Guid proxyId)
@ -34,26 +43,97 @@ public class ProxyOperations : StatefulOrm<Proxy, VmState>, IProxyOperations
return await data.FirstOrDefaultAsync(); return await data.FirstOrDefaultAsync();
} }
public System.Threading.Tasks.Task GetOrCreate(string region) public async System.Threading.Tasks.Task<Proxy?> GetOrCreate(string region)
{ {
throw new NotImplementedException(); var proxyList = QueryAsync(filter: $"region eq '{region}' and outdated eq false");
await foreach (var proxy in proxyList)
{
if (IsOutdated(proxy))
{
await Replace(proxy with { Outdated = true });
continue;
}
if (!VmStateHelper.Available().Contains(proxy.State))
{
continue;
}
return proxy;
}
_logTracer.Info($"creating proxy: region:{region}");
var newProxy = new Proxy(region, Guid.NewGuid(), DateTimeOffset.UtcNow, VmState.Init, Auth.BuildAuth(), null, null, _config.OnefuzzVersion, null, false);
await Replace(newProxy);
await _events.SendEvent(new EventProxyCreated(region, newProxy.ProxyId));
return newProxy;
} }
public bool IsAlive(Proxy proxy) public bool IsAlive(Proxy proxy)
{ {
throw new NotImplementedException(); var tenMinutesAgo = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10);
if (proxy.Heartbeat != null && proxy.Heartbeat.TimeStamp < tenMinutesAgo)
{
_logTracer.Info($"last heartbeat is more than an 10 minutes old: {proxy.Region} - last heartbeat:{proxy.Heartbeat} compared_to:{tenMinutesAgo}");
return false;
}
if (proxy.Heartbeat != null && proxy.TimeStamp != null && proxy.TimeStamp < tenMinutesAgo)
{
_logTracer.Error($"no heartbeat in the last 10 minutes: {proxy.Region} timestamp: {proxy.TimeStamp} compared_to:{tenMinutesAgo}");
return false;
}
return true;
} }
public bool IsOutdated(Proxy proxy) public bool IsOutdated(Proxy proxy)
{ {
throw new NotImplementedException(); if (!VmStateHelper.Available().Contains(proxy.State))
{
return false;
} }
public System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy) if (proxy.Version != _config.OnefuzzVersion)
{ {
throw new NotImplementedException(); _logTracer.Info($"mismatch version: proxy:{proxy.Version} service:{_config.OnefuzzVersion} state:{proxy.State}");
return true;
} }
if (proxy.CreatedTimestamp != null)
{
if (proxy.CreatedTimestamp < (DateTimeOffset.UtcNow - PROXY_LIFESPAN))
{
_logTracer.Info($"proxy older than 7 days:proxy-created:{proxy.CreatedTimestamp} state:{proxy.State}");
return true;
}
}
return false;
}
public async System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy)
{
var forwards = await GetForwards(proxy);
var url = (await _containers.GetFileSasUrl(new Container("proxy-configs"), $"{proxy.Region}/{proxy.ProxyId}/config.json", StorageType.Config, BlobSasPermissions.Read)).EnsureNotNull("Can't generate file sas");
var proxyConfig = new ProxyConfig(
Url: url,
Notification: _queue.GetQueueSas("proxy", StorageType.Config, QueueSasPermissions.Add).EnsureNotNull("can't generate queue sas"),
Region: proxy.Region,
ProxyId: proxy.ProxyId,
Forwards: forwards,
InstanceTelemetryKey: _config.ApplicationInsightsInstrumentationKey.EnsureNotNull("missing InstrumentationKey"),
MicrosoftTelemetryKey: _config.OneFuzzTelemetry.EnsureNotNull("missing Telemetry"),
InstanceId: await _containers.GetInstanceId());
await _containers.saveBlob(new Container("proxy-configs"), $"{proxy.Region}/{proxy.ProxyId}/config.json", _entityConverter.ToJsonString(proxyConfig), StorageType.Config);
}
public async Async.Task SetState(Proxy proxy, VmState state) public async Async.Task SetState(Proxy proxy, VmState state)
{ {
if (proxy.State == state) if (proxy.State == state)
@ -65,4 +145,23 @@ public class ProxyOperations : StatefulOrm<Proxy, VmState>, IProxyOperations
await _events.SendEvent(new EventProxyStateUpdated(proxy.Region, proxy.ProxyId, proxy.State)); await _events.SendEvent(new EventProxyStateUpdated(proxy.Region, proxy.ProxyId, proxy.State));
} }
public async Async.Task<List<Forward>> GetForwards(Proxy proxy)
{
var forwards = new List<Forward>();
await foreach (var entry in _proxyForwardOperations.SearchForward(region: proxy.Region, proxyId: proxy.ProxyId))
{
if (entry.EndTime < DateTimeOffset.UtcNow)
{
await _proxyForwardOperations.Delete(entry);
}
else
{
forwards.Add(new Forward(entry.Port, entry.DstPort, entry.DstIp));
}
}
return forwards;
}
} }

View File

@ -1,5 +1,6 @@
using Azure.Storage; using Azure.Storage;
using Azure.Storage.Queues; using Azure.Storage.Queues;
using Azure.Storage.Sas;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,6 +10,7 @@ public interface IQueue
{ {
Async.Task SendMessage(string name, byte[] message, StorageType storageType, TimeSpan? visibilityTimeout = null, TimeSpan? timeToLive = null); Async.Task SendMessage(string name, byte[] message, StorageType storageType, TimeSpan? visibilityTimeout = null, TimeSpan? timeToLive = null);
Async.Task<bool> QueueObject<T>(string name, T obj, StorageType storageType, TimeSpan? visibilityTimeout); Async.Task<bool> QueueObject<T>(string name, T obj, StorageType storageType, TimeSpan? visibilityTimeout);
Uri? GetQueueSas(string name, StorageType storageType, QueueSasPermissions permissions, TimeSpan? duration = null);
} }
@ -17,6 +19,8 @@ public class Queue : IQueue
IStorage _storage; IStorage _storage;
ILogTracer _log; ILogTracer _log;
static TimeSpan DEFAULT_DURATION = TimeSpan.FromDays(30);
public Queue(IStorage storage, ILogTracer log) public Queue(IStorage storage, ILogTracer log)
{ {
_storage = storage; _storage = storage;
@ -80,4 +84,12 @@ public class Queue : IQueue
return false; return false;
} }
} }
public Uri? GetQueueSas(string name, StorageType storageType, QueueSasPermissions permissions, TimeSpan? duration)
{
var queue = GetQueue(name, storageType) ?? throw new Exception($"unable to queue object, no such queue: {name}");
var sasaBuilder = new QueueSasBuilder(permissions, DateTimeOffset.UtcNow + (duration ?? DEFAULT_DURATION));
var url = queue.GenerateSasUri(sasaBuilder);
return url;
}
} }

View File

@ -41,7 +41,7 @@ public class Reports : IReports
return null; return null;
} }
return ParseReportOrRegression(blob, filePath, expectReports); return ParseReportOrRegression(blob.ToString(), filePath, expectReports);
} }
private RegressionReportOrReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false) private RegressionReportOrReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false)

View File

@ -1,6 +1,8 @@
using Azure.ResourceManager.Network; using Azure.ResourceManager.Network;
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
public interface ISubnet public interface ISubnet
{ {
System.Threading.Tasks.Task<VirtualNetworkResource?> GetVnet(string vnetName); System.Threading.Tasks.Task<VirtualNetworkResource?> GetVnet(string vnetName);

View File

@ -1,4 +1,4 @@
using Azure.Data.Tables; using Azure.Data.Tables;
using System.Reflection; using System.Reflection;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text.Json; using System.Text.Json;
@ -14,7 +14,7 @@ public abstract record EntityBase
public DateTimeOffset? TimeStamp { get; set; } public DateTimeOffset? TimeStamp { get; set; }
} }
public abstract record StatefulEntityBase<T>([property: JsonIgnore] T state) : EntityBase() where T : Enum; public abstract record StatefulEntityBase<T>([property: JsonIgnore] T State) : EntityBase() where T : Enum;
/// Indicates that the enum cases should no be renamed /// Indicates that the enum cases should no be renamed
[AttributeUsage(AttributeTargets.Enum)] [AttributeUsage(AttributeTargets.Enum)]
@ -147,7 +147,7 @@ public class EntityConverter
}); });
} }
public string ToJsonString<T>(T typedEntity) where T : EntityBase public string ToJsonString<T>(T typedEntity)
{ {
var serialized = JsonSerializer.Serialize(typedEntity, _options); var serialized = JsonSerializer.Serialize(typedEntity, _options);
return serialized; return serialized;

View File

@ -20,14 +20,13 @@ namespace ApiService.OneFuzzLib.Orm
} }
public class Orm<T> : IOrm<T> where T : EntityBase public class Orm<T> : IOrm<T> where T : EntityBase
{ {
IStorage _storage; protected readonly IStorage _storage;
EntityConverter _entityConverter; protected readonly EntityConverter _entityConverter;
IServiceConfig _config; protected readonly ILogTracer _logTracer;
protected ILogTracer _logTracer;
protected readonly IServiceConfig _config;
public Orm(IStorage storage, ILogTracer logTracer, IServiceConfig config) public Orm(IStorage storage, ILogTracer logTracer, IServiceConfig config)
@ -179,7 +178,7 @@ namespace ApiService.OneFuzzLib.Orm
/// <returns></returns> /// <returns></returns>
public async System.Threading.Tasks.Task<T?> ProcessStateUpdate(T entity) public async System.Threading.Tasks.Task<T?> ProcessStateUpdate(T entity)
{ {
TState state = entity.state; TState state = entity.State;
var func = _stateFuncs.GetOrAdd(state.ToString(), (string k) => var func = _stateFuncs.GetOrAdd(state.ToString(), (string k) =>
typeof(T).GetMethod(k) switch typeof(T).GetMethod(k) switch
{ {
@ -206,13 +205,13 @@ namespace ApiService.OneFuzzLib.Orm
{ {
for (int i = 0; i < MaxUpdates; i++) for (int i = 0; i < MaxUpdates; i++)
{ {
var state = entity.state; var state = entity.State;
var newEntity = await ProcessStateUpdate(entity); var newEntity = await ProcessStateUpdate(entity);
if (newEntity == null) if (newEntity == null)
return null; return null;
if (newEntity.state.Equals(state)) if (newEntity.State.Equals(state))
{ {
return newEntity; return newEntity;
} }

View File

@ -128,6 +128,12 @@
"System.Text.Json": "4.7.2" "System.Text.Json": "4.7.2"
} }
}, },
"Faithlife.Utility": {
"type": "Direct",
"requested": "[0.12.2, )",
"resolved": "0.12.2",
"contentHash": "JgMAGj8ekeAzKkagubXqf1UqgfHq89GyA1UQYWbkAe441uRr2Rh2rktkx5Z0LPwmD/aOqu9cxjekD2GZjP8rbw=="
},
"Microsoft.Azure.Functions.Worker": { "Microsoft.Azure.Functions.Worker": {
"type": "Direct", "type": "Direct",
"requested": "[1.6.0, )", "requested": "[1.6.0, )",

View File

@ -71,13 +71,17 @@ namespace Tests
public static Gen<ProxyForward> ProxyForward() public static Gen<ProxyForward> ProxyForward()
{ {
return Arb.Generate<Tuple<string, int, int, IPv4Address>>().Select( return Arb.Generate<Tuple<Tuple<string, int, Guid, Guid, Guid?, int>, Tuple<IPv4Address, DateTimeOffset>>>().Select(
arg => arg =>
new ProxyForward( new ProxyForward(
Region: arg.Item1, Region: arg.Item1.Item1,
DstPort: arg.Item2, Port: arg.Item1.Item2,
SrcPort: arg.Item3, ScalesetId: arg.Item1.Item3,
DstIp: arg.Item4.Item.ToString() MachineId: arg.Item1.Item4,
ProxyId: arg.Item1.Item5,
DstPort: arg.Item1.Item6,
DstIp: arg.Item2.Item1.ToString(),
EndTime: arg.Item2.Item2
) )
); );
} }
@ -594,6 +598,8 @@ namespace Tests
} }
/* /*
//Sample function on how repro a failing test run, using Replay //Sample function on how repro a failing test run, using Replay
//functionality of FsCheck. Feel free to //functionality of FsCheck. Feel free to