diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index c1186f3b6c..f56fac7967 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -8,6 +8,7 @@ 5 + diff --git a/src/ApiService/ApiService/OneFuzzTypes/Events.cs b/src/ApiService/ApiService/OneFuzzTypes/Events.cs index 7f0365044f..205b875826 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Events.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Events.cs @@ -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; diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index 1c54b0dd75..acbf6b3797 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -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 @@ bool DebugKeepNode ) : StatefulEntityBase(State); -public partial record ProxyForward +public record Forward ( - [PartitionKey] Region Region, - [RowKey] int DstPort, int SrcPort, + int DstPort, string DstIp +); + + +public record ProxyForward +( + [PartitionKey] Region Region, + int Port, + Guid ScalesetId, + Guid MachineId, + Guid? ProxyId, + [RowKey] int DstPort, + string DstIp, + DateTimeOffset EndTime ) : EntityBase(); -public partial record ProxyConfig +public record ProxyConfig ( Uri Url, - string Notification, + Uri Notification, Region Region, Guid? ProxyId, - List Forwards, + List Forwards, string InstanceTelemetryKey, - string MicrosoftTelemetryKey + string MicrosoftTelemetryKey, + Guid InstanceId ); -public partial record Proxy +public record Proxy ( [PartitionKey] Region Region, [RowKey] Guid ProxyId, diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index f326659bd3..50a5315274 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -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; diff --git a/src/ApiService/ApiService/ServiceConfiguration.cs b/src/ApiService/ApiService/ServiceConfiguration.cs index 7177a73edd..f82a8e7606 100644 --- a/src/ApiService/ApiService/ServiceConfiguration.cs +++ b/src/ApiService/ApiService/ServiceConfiguration.cs @@ -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 ServiceConfiguration() 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"; } } \ No newline at end of file diff --git a/src/ApiService/ApiService/onefuzzlib/Auth.cs b/src/ApiService/ApiService/onefuzzlib/Auth.cs new file mode 100644 index 0000000000..67b8473c14 --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/Auth.cs @@ -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); + } +} diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs index 184c63e26a..e71e1dda83 100644 --- a/src/ApiService/ApiService/onefuzzlib/Containers.cs +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -3,17 +3,20 @@ using Azure.Storage.Blobs; using Azure.Storage; using Azure; +using Azure.Storage.Sas; namespace Microsoft.OneFuzz.Service; + public interface IContainers { - public Task?> GetBlob(Container container, string name, StorageType storageType); + public Task GetBlob(Container container, string name, StorageType storageType); public Async.Task 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 GetFileSasUrl(Container container, string name, StorageType storageType, BlobSasPermissions permissions, TimeSpan? duration = null); + Async.Task saveBlob(Container container, string v1, string v2, StorageType config); + Task GetInstanceId(); } public class Containers : IContainers @@ -29,7 +32,7 @@ public Containers(ILogTracer log, IStorage storage, ICreds creds) _creds = creds; _armClient = creds.ArmClient; } - public async Task?> GetBlob(Container container, string name, StorageType storageType) + public async Task GetBlob(Container container, string name, StorageType storageType) { var client = await FindContainer(container, storageType); @@ -41,7 +44,7 @@ public Containers(ILogTracer log, IStorage storage, ICreds creds) try { return (await client.GetBlobClient(name).DownloadContentAsync()) - .Value.Content.ToArray(); + .Value.Content; } catch (RequestFailedException) { @@ -85,9 +88,59 @@ private static Uri GetUrl(string accountName) 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 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 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()); } } diff --git a/src/ApiService/ApiService/onefuzzlib/InstanceConfig.cs b/src/ApiService/ApiService/onefuzzlib/InstanceConfig.cs index d462f5f95e..cabdf93447 100644 --- a/src/ApiService/ApiService/onefuzzlib/InstanceConfig.cs +++ b/src/ApiService/ApiService/onefuzzlib/InstanceConfig.cs @@ -15,12 +15,11 @@ public class ConfigOperations : Orm, 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 Fetch() diff --git a/src/ApiService/ApiService/onefuzzlib/Network.cs b/src/ApiService/ApiService/onefuzzlib/Network.cs index e69df06d7d..77e9a21e80 100644 --- a/src/ApiService/ApiService/onefuzzlib/Network.cs +++ b/src/ApiService/ApiService/onefuzzlib/Network.cs @@ -25,12 +25,6 @@ public Network(string region, string group, string name, NetworkConfig networkCo _subnet = subnet; } - - private static Guid GenerateGuidv5(Guid nameSpace, string name) - { - throw new NotImplementedException(); - } - public static async Async.Task Create(string region, ICreds creds, IConfigOperations configOperations, ISubnet subnet) { var group = creds.GetBaseResourceGroup(); @@ -50,7 +44,7 @@ public static async Async.Task Create(string region, ICreds creds, ICon } 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}"; } diff --git a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs index 36d1291973..1f4cdf6882 100644 --- a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs @@ -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, INotificationOperations { - private ILogTracer _log; private IReports _reports; private ITaskOperations _taskOperations; @@ -23,7 +23,7 @@ public class NotificationOperations : Orm, 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 async Async.Task NewFiles(Container container, string filename, bool fail { 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 IAsyncEnumerable GetNotifications(Container container) 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; } diff --git a/src/ApiService/ApiService/onefuzzlib/Nsg.cs b/src/ApiService/ApiService/onefuzzlib/Nsg.cs index d87b48bf89..1b96710389 100644 --- a/src/ApiService/ApiService/onefuzzlib/Nsg.cs +++ b/src/ApiService/ApiService/onefuzzlib/Nsg.cs @@ -72,8 +72,10 @@ public bool OkToDelete(HashSet active_regions, string nsg_region, string 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. + /// + /// Returns True if deletion completed (thus resource not found) or successfully started. + /// Returns False if failed to start deletion. + /// public async Async.Task StartDeleteNsg(string name) { _logTracer.Info($"deleting nsg: {name}"); diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs new file mode 100644 index 0000000000..f5ee767975 --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs @@ -0,0 +1,34 @@ +using ApiService.OneFuzzLib.Orm; + +namespace Microsoft.OneFuzz.Service; + + +public interface IProxyForwardOperations : IOrm +{ + IAsyncEnumerable SearchForward(Guid? scalesetId = null, string? region = null, Guid? machineId = null, Guid? proxyId = null, int? dstPort = null); +} + + +public class ProxyForwardOperations : Orm, IProxyForwardOperations +{ + public ProxyForwardOperations(IStorage storage, ILogTracer logTracer, IServiceConfig config) : base(storage, logTracer, config) + { + } + + public IAsyncEnumerable 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); + } +} diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index af1db72589..4f36e7f24e 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -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 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 GetOrCreate(string region); } public class ProxyOperations : StatefulOrm, 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 GetByProxyId(Guid proxyId) @@ -34,26 +43,97 @@ public ProxyOperations(ILogTracer log, IStorage storage, IEvents events, IServic return await data.FirstOrDefaultAsync(); } - public System.Threading.Tasks.Task GetOrCreate(string region) + public async System.Threading.Tasks.Task 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; + } + + if (proxy.Version != _config.OnefuzzVersion) + { + _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 System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy) + public async System.Threading.Tasks.Task SaveProxyConfig(Proxy proxy) { - throw new NotImplementedException(); + 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 async Async.Task SetState(Proxy proxy, VmState state) await _events.SendEvent(new EventProxyStateUpdated(proxy.Region, proxy.ProxyId, proxy.State)); } + + + public async Async.Task> GetForwards(Proxy proxy) + { + var forwards = new List(); + + 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; + } } diff --git a/src/ApiService/ApiService/onefuzzlib/Queue.cs b/src/ApiService/ApiService/onefuzzlib/Queue.cs index bce5fb786d..648825528a 100644 --- a/src/ApiService/ApiService/onefuzzlib/Queue.cs +++ b/src/ApiService/ApiService/onefuzzlib/Queue.cs @@ -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 QueueObject(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 async Task QueueObject(string name, T obj, StorageType storageTy 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; + } } diff --git a/src/ApiService/ApiService/onefuzzlib/Reports.cs b/src/ApiService/ApiService/onefuzzlib/Reports.cs index a60ad3d16b..19622abba9 100644 --- a/src/ApiService/ApiService/onefuzzlib/Reports.cs +++ b/src/ApiService/ApiService/onefuzzlib/Reports.cs @@ -41,7 +41,7 @@ public Reports(ILogTracer log, IContainers containers) return null; } - return ParseReportOrRegression(blob, filePath, expectReports); + return ParseReportOrRegression(blob.ToString(), filePath, expectReports); } private RegressionReportOrReport? ParseReportOrRegression(string content, string? filePath, bool expectReports = false) diff --git a/src/ApiService/ApiService/onefuzzlib/Subnet.cs b/src/ApiService/ApiService/onefuzzlib/Subnet.cs index ef06a3bf6d..6796b5a769 100644 --- a/src/ApiService/ApiService/onefuzzlib/Subnet.cs +++ b/src/ApiService/ApiService/onefuzzlib/Subnet.cs @@ -1,6 +1,8 @@ using Azure.ResourceManager.Network; + namespace Microsoft.OneFuzz.Service; + public interface ISubnet { System.Threading.Tasks.Task GetVnet(string vnetName); diff --git a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs index f8fbe12980..63391aa6d6 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs @@ -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([property: JsonIgnore] T state) : EntityBase() where T : Enum; +public abstract record StatefulEntityBase([property: JsonIgnore] T State) : EntityBase() where T : Enum; /// Indicates that the enum cases should no be renamed [AttributeUsage(AttributeTargets.Enum)] @@ -147,7 +147,7 @@ private EntityInfo GetEntityInfo() }); } - public string ToJsonString(T typedEntity) where T : EntityBase + public string ToJsonString(T typedEntity) { var serialized = JsonSerializer.Serialize(typedEntity, _options); return serialized; diff --git a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs index e8c42e5107..51b0f4f7ff 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs @@ -20,14 +20,13 @@ public interface IOrm where T : EntityBase } - - public class Orm : IOrm 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 @@ public StatefulOrm(IStorage storage, ILogTracer logTracer, IServiceConfig config /// public async System.Threading.Tasks.Task 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 @@ public StatefulOrm(IStorage storage, ILogTracer logTracer, IServiceConfig config { 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; } diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index 80de00828c..8544dc1224 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -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, )", diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index 0445a7c3af..c240c9fe8d 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -71,13 +71,17 @@ public static Gen Node() public static Gen ProxyForward() { - return Arb.Generate>().Select( + return Arb.Generate, Tuple>>().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,9 +598,11 @@ public bool Notification(Notification n) } + + /* //Sample function on how repro a failing test run, using Replay - //functionality of FsCheck. Feel free to + //functionality of FsCheck. Feel free to [Property] void Replay() { @@ -845,7 +851,7 @@ public bool RegressionReportOrReport(RegressionReportOrReport e) /* //Sample function on how repro a failing test run, using Replay - //functionality of FsCheck. Feel free to + //functionality of FsCheck. Feel free to [Property] void Replay() {