mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-14 19:08:08 +00:00
migrate timer_proxy part 2 (#1836)
This commit is contained in:
@ -8,6 +8,7 @@
|
||||
<WarningLevel>5</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<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.EventGrid" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
|
||||
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using PoolName = System.String;
|
||||
|
@ -95,7 +95,7 @@ public record ProxyHeartbeat
|
||||
DateTimeOffset TimeStamp
|
||||
);
|
||||
|
||||
public partial record Node
|
||||
public record Node
|
||||
(
|
||||
DateTimeOffset? InitializedAt,
|
||||
[PartitionKey] PoolName PoolName,
|
||||
@ -111,27 +111,40 @@ public partial record Node
|
||||
) : StatefulEntityBase<NodeState>(State);
|
||||
|
||||
|
||||
public partial record ProxyForward
|
||||
public record Forward
|
||||
(
|
||||
int SrcPort,
|
||||
int DstPort,
|
||||
string DstIp
|
||||
);
|
||||
|
||||
|
||||
public record ProxyForward
|
||||
(
|
||||
[PartitionKey] Region Region,
|
||||
int Port,
|
||||
Guid ScalesetId,
|
||||
Guid MachineId,
|
||||
Guid? ProxyId,
|
||||
[RowKey] int DstPort,
|
||||
int SrcPort,
|
||||
string DstIp
|
||||
string DstIp,
|
||||
DateTimeOffset EndTime
|
||||
) : EntityBase();
|
||||
|
||||
public partial record ProxyConfig
|
||||
public record ProxyConfig
|
||||
(
|
||||
Uri Url,
|
||||
string Notification,
|
||||
Uri Notification,
|
||||
Region Region,
|
||||
Guid? ProxyId,
|
||||
List<ProxyForward> Forwards,
|
||||
List<Forward> Forwards,
|
||||
string InstanceTelemetryKey,
|
||||
string MicrosoftTelemetryKey
|
||||
string MicrosoftTelemetryKey,
|
||||
Guid InstanceId
|
||||
|
||||
);
|
||||
|
||||
public partial record Proxy
|
||||
public record Proxy
|
||||
(
|
||||
[PartitionKey] Region Region,
|
||||
[RowKey] Guid ProxyId,
|
||||
|
@ -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 System;
|
||||
|
@ -36,6 +36,8 @@ public interface IServiceConfig
|
||||
|
||||
public string? OneFuzzResourceGroup { get; }
|
||||
public string? OneFuzzTelemetry { get; }
|
||||
|
||||
public string OnefuzzVersion { get; }
|
||||
}
|
||||
|
||||
public class ServiceConfiguration : IServiceConfig
|
||||
@ -79,4 +81,5 @@ public class ServiceConfiguration : IServiceConfig
|
||||
public string? OneFuzzOwner { get => Environment.GetEnvironmentVariable("ONEFUZZ_OWNER"); }
|
||||
public string? OneFuzzResourceGroup { get => Environment.GetEnvironmentVariable("ONEFUZZ_RESOURCE_GROUP"); }
|
||||
public string? OneFuzzTelemetry { get => Environment.GetEnvironmentVariable("ONEFUZZ_TELEMETRY"); }
|
||||
public string OnefuzzVersion { get => Environment.GetEnvironmentVariable("ONEFUZZ_VERSION") ?? "0.0.0"; }
|
||||
}
|
16
src/ApiService/ApiService/onefuzzlib/Auth.cs
Normal file
16
src/ApiService/ApiService/onefuzzlib/Auth.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -3,17 +3,20 @@ using Azure.ResourceManager;
|
||||
using Azure.Storage.Blobs;
|
||||
using Azure.Storage;
|
||||
using Azure;
|
||||
using Azure.Storage.Sas;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
|
||||
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 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
|
||||
@ -29,7 +32,7 @@ public class Containers : IContainers
|
||||
_creds = creds;
|
||||
_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);
|
||||
|
||||
@ -41,7 +44,7 @@ public class Containers : IContainers
|
||||
try
|
||||
{
|
||||
return (await client.GetBlobClient(name).DownloadContentAsync())
|
||||
.Value.Content.ToArray();
|
||||
.Value.Content;
|
||||
}
|
||||
catch (RequestFailedException)
|
||||
{
|
||||
@ -85,9 +88,59 @@ public class Containers : IContainers
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,11 @@ public class ConfigOperations : Orm<InstanceConfig>, IConfigOperations
|
||||
{
|
||||
private readonly IEvents _events;
|
||||
private readonly ILogTracer _log;
|
||||
private readonly IServiceConfig _config;
|
||||
|
||||
public ConfigOperations(IStorage storage, IEvents events, ILogTracer log, IServiceConfig config) : base(storage, log, config)
|
||||
{
|
||||
_events = events;
|
||||
_log = log;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public async Task<InstanceConfig> Fetch()
|
||||
|
@ -25,12 +25,6 @@ public partial class TimerProxy
|
||||
_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)
|
||||
{
|
||||
var group = creds.GetBaseResourceGroup();
|
||||
@ -50,7 +44,7 @@ public partial class TimerProxy
|
||||
}
|
||||
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}";
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using ApiService.OneFuzzLib.Orm;
|
||||
using Azure.Storage.Sas;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
@ -10,7 +11,6 @@ public interface INotificationOperations
|
||||
|
||||
public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
{
|
||||
private ILogTracer _log;
|
||||
private IReports _reports;
|
||||
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)
|
||||
: base(storage, log, config)
|
||||
{
|
||||
_log = log;
|
||||
|
||||
_reports = reports;
|
||||
_taskOperations = taskOperations;
|
||||
_containers = containers;
|
||||
@ -76,9 +76,9 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
{
|
||||
if (containers.Contains(container.ContainerName))
|
||||
{
|
||||
_log.Info($"queuing input {container.ContainerName} {filename} {task.TaskId}");
|
||||
var url = _containers.GetFileSasUrl(container, filename, StorageType.Corpus, read: true, delete: true);
|
||||
await _queue.SendMessage(task.TaskId.ToString(), System.Text.Encoding.UTF8.GetBytes(url.ToString()), StorageType.Corpus);
|
||||
_logTracer.Info($"queuing input {container.ContainerName} {filename} {task.TaskId}");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -72,8 +72,10 @@ namespace Microsoft.OneFuzz.Service
|
||||
return !active_regions.Contains(nsg_region) && nsg_region == nsg_name;
|
||||
}
|
||||
|
||||
// Returns True if deletion completed (thus resource not found) or successfully started.
|
||||
// Returns False if failed to start deletion.
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
_logTracer.Info($"deleting nsg: {name}");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using ApiService.OneFuzzLib.Orm;
|
||||
using Azure.Storage.Sas;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
@ -11,19 +12,27 @@ public interface IProxyOperations : IStatefulOrm<Proxy, VmState>
|
||||
bool IsAlive(Proxy proxy);
|
||||
System.Threading.Tasks.Task SaveProxyConfig(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
|
||||
{
|
||||
private readonly ILogTracer _log;
|
||||
|
||||
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)
|
||||
: base(storage, log, config)
|
||||
static TimeSpan PROXY_LIFESPAN = TimeSpan.FromDays(7);
|
||||
|
||||
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;
|
||||
_proxyForwardOperations = proxyForwardOperations;
|
||||
_containers = containers;
|
||||
_queue = queue;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public async Task<Proxy?> GetByProxyId(Guid proxyId)
|
||||
@ -34,26 +43,97 @@ public class ProxyOperations : StatefulOrm<Proxy, VmState>, IProxyOperations
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Azure.Storage;
|
||||
using Azure.Storage.Queues;
|
||||
using Azure.Storage.Sas;
|
||||
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
|
||||
using System.Text.Json;
|
||||
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<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;
|
||||
ILogTracer _log;
|
||||
|
||||
static TimeSpan DEFAULT_DURATION = TimeSpan.FromDays(30);
|
||||
|
||||
public Queue(IStorage storage, ILogTracer log)
|
||||
{
|
||||
_storage = storage;
|
||||
@ -80,4 +84,12 @@ public class Queue : IQueue
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class Reports : IReports
|
||||
return null;
|
||||
}
|
||||
|
||||
return ParseReportOrRegression(blob, filePath, expectReports);
|
||||
return ParseReportOrRegression(blob.ToString(), filePath, expectReports);
|
||||
}
|
||||
|
||||
private RegressionReportOrReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false)
|
||||
|
@ -1,6 +1,8 @@
|
||||
using Azure.ResourceManager.Network;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
|
||||
public interface ISubnet
|
||||
{
|
||||
System.Threading.Tasks.Task<VirtualNetworkResource?> GetVnet(string vnetName);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Azure.Data.Tables;
|
||||
using Azure.Data.Tables;
|
||||
using System.Reflection;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.Json;
|
||||
@ -14,7 +14,7 @@ public abstract record EntityBase
|
||||
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
|
||||
[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);
|
||||
return serialized;
|
||||
|
@ -20,14 +20,13 @@ namespace ApiService.OneFuzzLib.Orm
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class Orm<T> : IOrm<T> where T : EntityBase
|
||||
{
|
||||
IStorage _storage;
|
||||
EntityConverter _entityConverter;
|
||||
IServiceConfig _config;
|
||||
protected ILogTracer _logTracer;
|
||||
protected readonly IStorage _storage;
|
||||
protected readonly EntityConverter _entityConverter;
|
||||
protected readonly ILogTracer _logTracer;
|
||||
|
||||
protected readonly IServiceConfig _config;
|
||||
|
||||
|
||||
public Orm(IStorage storage, ILogTracer logTracer, IServiceConfig config)
|
||||
@ -179,7 +178,7 @@ namespace ApiService.OneFuzzLib.Orm
|
||||
/// <returns></returns>
|
||||
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) =>
|
||||
typeof(T).GetMethod(k) switch
|
||||
{
|
||||
@ -206,13 +205,13 @@ namespace ApiService.OneFuzzLib.Orm
|
||||
{
|
||||
for (int i = 0; i < MaxUpdates; i++)
|
||||
{
|
||||
var state = entity.state;
|
||||
var state = entity.State;
|
||||
var newEntity = await ProcessStateUpdate(entity);
|
||||
|
||||
if (newEntity == null)
|
||||
return null;
|
||||
|
||||
if (newEntity.state.Equals(state))
|
||||
if (newEntity.State.Equals(state))
|
||||
{
|
||||
return newEntity;
|
||||
}
|
||||
|
@ -128,6 +128,12 @@
|
||||
"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": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.6.0, )",
|
||||
|
@ -71,13 +71,17 @@ namespace Tests
|
||||
|
||||
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 =>
|
||||
new ProxyForward(
|
||||
Region: arg.Item1,
|
||||
DstPort: arg.Item2,
|
||||
SrcPort: arg.Item3,
|
||||
DstIp: arg.Item4.Item.ToString()
|
||||
Region: arg.Item1.Item1,
|
||||
Port: arg.Item1.Item2,
|
||||
ScalesetId: arg.Item1.Item3,
|
||||
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
|
||||
//functionality of FsCheck. Feel free to
|
||||
|
Reference in New Issue
Block a user