diff --git a/src/ApiService/.editorconfig b/src/ApiService/.editorconfig index a8284cde1..1b2340b18 100644 --- a/src/ApiService/.editorconfig +++ b/src/ApiService/.editorconfig @@ -162,3 +162,6 @@ dotnet_diagnostic.CA1848.severity = none # allow throwing base "Exception" class, since it's done a lot # TODO: improve this dotnet_diagnostic.CA2201.severity = suggestion + +# ignore this custom diagnostic about "complex" code +dotnet_diagnostic.CSE006.severity = none diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index 1135ba821..3e42f4751 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -44,6 +44,7 @@ + @@ -60,4 +61,8 @@ PreserveNewest + + + + diff --git a/src/ApiService/ApiService/Functions/AgentCommands.cs b/src/ApiService/ApiService/Functions/AgentCommands.cs index 6a6d66a5e..0381d4f30 100644 --- a/src/ApiService/ApiService/Functions/AgentCommands.cs +++ b/src/ApiService/ApiService/Functions/AgentCommands.cs @@ -52,7 +52,7 @@ public class AgentCommands { var message = await _context.NodeMessageOperations.GetEntityAsync(nodeCommand.MachineId.ToString(), nodeCommand.MessageId); if (message != null) { - await _context.NodeMessageOperations.Delete(message); + await _context.NodeMessageOperations.Delete(message).IgnoreResult(); } else { _log.WithTag("Command", "DELETE").Verbose($"failed to find machine id {nodeCommand.MachineId} for message {nodeCommand.MessageId}"); } diff --git a/src/ApiService/ApiService/Functions/AgentEvents.cs b/src/ApiService/ApiService/Functions/AgentEvents.cs index 51f2ff036..74dbd2a3d 100644 --- a/src/ApiService/ApiService/Functions/AgentEvents.cs +++ b/src/ApiService/ApiService/Functions/AgentEvents.cs @@ -73,12 +73,14 @@ public class AgentEvents { if (ev.State == NodeState.Free) { if (node.ReimageRequested || node.DeleteRequested) { _log.Info($"stopping free node with reset flags: {machineId}"); + // discard result: node not used after this point _ = await _context.NodeOperations.Stop(node); return null; } if (await _context.NodeOperations.CouldShrinkScaleset(node)) { _log.Info($"stopping free node to resize scaleset: {machineId}"); + // discard result: node not used after this point _ = await _context.NodeOperations.SetHalt(node); return null; } @@ -87,6 +89,7 @@ public class AgentEvents { if (ev.State == NodeState.Init) { if (node.DeleteRequested) { _log.Info($"stopping node (init and delete_requested): {machineId}"); + // discard result: node not used after this point _ = await _context.NodeOperations.Stop(node); return null; } @@ -95,6 +98,7 @@ public class AgentEvents { // they send 'init' with reimage_requested, it's because the node was reimaged // successfully. node = node with { ReimageRequested = false, InitializedAt = DateTimeOffset.UtcNow }; + // discard result: node not used after this point _ = await _context.NodeOperations.SetState(node, ev.State); return null; } @@ -129,7 +133,7 @@ public class AgentEvents { // Other states we would want to preserve are excluded by the // outermost conditional check. if (task.State != TaskState.Running && task.State != TaskState.SettingUp) { - await _context.TaskOperations.SetState(task, TaskState.SettingUp); + task = await _context.TaskOperations.SetState(task, TaskState.SettingUp); } var nodeTask = new NodeTasks( @@ -155,7 +159,8 @@ public class AgentEvents { // if tasks are running on the node when it reports as Done // those are stopped early await _context.NodeOperations.MarkTasksStoppedEarly(node, error); - await _context.NodeOperations.ToReimage(node, done: true); + // discard result: node not used after this point + _ = await _context.NodeOperations.ToReimage(node, done: true); } return null; @@ -193,8 +198,8 @@ public class AgentEvents { } if (!node.State.ReadyForReset()) { + // discard result: node not used after this point _ = await _context.NodeOperations.SetState(node, NodeState.Busy); - // node unused after this point } var nodeTask = new NodeTasks( @@ -212,7 +217,7 @@ public class AgentEvents { } _log.Info($"task started on node. machine_id:{machineId} job_id:{task.JobId} task_id:{task.TaskId}"); - await _context.TaskOperations.SetState(task, TaskState.Running); + task = await _context.TaskOperations.SetState(task, TaskState.Running); var taskEvent = new TaskEvent( TaskId: task.TaskId, diff --git a/src/ApiService/ApiService/Functions/Pool.cs b/src/ApiService/ApiService/Functions/Pool.cs index 3197aef13..d9fefc49f 100644 --- a/src/ApiService/ApiService/Functions/Pool.cs +++ b/src/ApiService/ApiService/Functions/Pool.cs @@ -41,7 +41,8 @@ public class Pool { return await _context.RequestHandling.NotOk(r, poolResult.ErrorV, "pool stop"); } - await _context.PoolOperations.SetShutdown(poolResult.OkV, Now: request.OkV.Now); + // discard result: not used after this point + _ = await _context.PoolOperations.SetShutdown(poolResult.OkV, Now: request.OkV.Now); return await RequestHandling.Ok(r, true); } diff --git a/src/ApiService/ApiService/Functions/Proxy.cs b/src/ApiService/ApiService/Functions/Proxy.cs index b44aba6fb..431d6a2c3 100644 --- a/src/ApiService/ApiService/Functions/Proxy.cs +++ b/src/ApiService/ApiService/Functions/Proxy.cs @@ -134,14 +134,18 @@ public class Proxy { "ProxyReset"); } - var proxyList = await _context.ProxyOperations.SearchByPartitionKeys(new[] { $"{request.OkV.Region}" }).ToListAsync(); - - foreach (var proxy in proxyList) { - await _context.ProxyOperations.SetState(proxy, VmState.Stopping); + bool any = false; + { + var proxyList = _context.ProxyOperations.SearchByPartitionKeys(new[] { $"{request.OkV.Region}" }); + await foreach (var proxy in proxyList) { + any = true; + // ignoring result, proxyList not used outside this block + _ = await _context.ProxyOperations.SetState(proxy, VmState.Stopping); + } } var response = req.CreateResponse(HttpStatusCode.OK); - await response.WriteAsJsonAsync(new BoolResult(proxyList.Any())); + await response.WriteAsJsonAsync(new BoolResult(any)); return response; } diff --git a/src/ApiService/ApiService/Functions/Scaleset.cs b/src/ApiService/ApiService/Functions/Scaleset.cs index 1d01657b3..cc41e4b13 100644 --- a/src/ApiService/ApiService/Functions/Scaleset.cs +++ b/src/ApiService/ApiService/Functions/Scaleset.cs @@ -43,6 +43,7 @@ public class Scaleset { } var scaleset = scalesetResult.OkV; + // result ignored: not used after this point _ = await _context.ScalesetOperations.SetShutdown(scaleset, request.OkV.Now); return await RequestHandling.Ok(req, true); } diff --git a/src/ApiService/ApiService/Functions/TimerProxy.cs b/src/ApiService/ApiService/Functions/TimerProxy.cs index e257280cc..b2621a8a5 100644 --- a/src/ApiService/ApiService/Functions/TimerProxy.cs +++ b/src/ApiService/ApiService/Functions/TimerProxy.cs @@ -16,7 +16,7 @@ public class TimerProxy { var proxyOperations = _context.ProxyOperations; var scalesetOperations = _context.ScalesetOperations; - var nsgOpertions = _context.NsgOperations; + var nsgOperations = _context.NsgOperations; var proxies = await proxyOperations.QueryAsync().ToListAsync(); @@ -69,14 +69,14 @@ public class TimerProxy { // since we do not support bring your own NSG var nsgName = Nsg.NameFromRegion(region); - if (await nsgOpertions.GetNsg(nsgName) != null) { + if (await nsgOperations.GetNsg(nsgName) != null) { var network = await Network.Init(region, _context); var subnet = await network.GetSubnet(); if (subnet != null) { var vnet = await network.GetVnet(); if (vnet != null) { - var result = await nsgOpertions.AssociateSubnet(nsgName, vnet, subnet); + var result = await nsgOperations.AssociateSubnet(nsgName, vnet, subnet); if (!result.OkV) { _logger.Error($"Failed to associate NSG and subnet due to {result.ErrorV} in region {region}"); } @@ -86,10 +86,10 @@ public class TimerProxy { } // if there are NSGs with name same as the region that they are allocated // and have no NIC associated with it then delete the NSG - await foreach (var nsg in nsgOpertions.ListNsgs()) { - if (nsgOpertions.OkToDelete(regions, nsg.Data.Location!, nsg.Data.Name)) { + await foreach (var nsg in nsgOperations.ListNsgs()) { + if (nsgOperations.OkToDelete(regions, nsg.Data.Location!, nsg.Data.Name)) { if (nsg.Data.NetworkInterfaces.Count == 0 && nsg.Data.Subnets.Count == 0) { - if (!await nsgOpertions.StartDeleteNsg(nsg.Data.Name)) { + if (!await nsgOperations.StartDeleteNsg(nsg.Data.Name)) { _logger.Warning($"failed to start deleting NSG {nsg.Data.Name}"); } } diff --git a/src/ApiService/ApiService/Functions/TimerRepro.cs b/src/ApiService/ApiService/Functions/TimerRepro.cs index f626c7c1d..b10be012f 100644 --- a/src/ApiService/ApiService/Functions/TimerRepro.cs +++ b/src/ApiService/ApiService/Functions/TimerRepro.cs @@ -14,20 +14,25 @@ public class TimerRepro { [Function("TimerRepro")] public async Async.Task Run([TimerTrigger("00:00:30")] TimerInfo myTimer) { - var expired = _onefuzzContext.ReproOperations.SearchExpired(); - await foreach (var repro in expired) { - _log.Info($"stopping repro: {repro.VmId}"); - _ = await _onefuzzContext.ReproOperations.Stopping(repro); + var expiredVmIds = new HashSet(); + { + var expired = _onefuzzContext.ReproOperations.SearchExpired(); + await foreach (var repro in expired) { + _log.Info($"stopping repro: {repro.VmId}"); + _ = expiredVmIds.Add(repro.VmId); + // ignoring result: value not used later + _ = await _onefuzzContext.ReproOperations.Stopping(repro); + } } - var expiredVmIds = expired.Select(repro => repro?.VmId); - await foreach (var repro in _onefuzzContext.ReproOperations.SearchStates(VmStateHelper.NeedsWork)) { - if (await expiredVmIds.ContainsAsync(repro.VmId)) { + if (expiredVmIds.Contains(repro.VmId)) { // this VM already got processed during the expired phase continue; } + _log.Info($"update repro: {repro.VmId}"); + // ignoring result: value not used later _ = await _onefuzzContext.ReproOperations.ProcessStateUpdates(repro); } } diff --git a/src/ApiService/ApiService/Functions/TimerRetention.cs b/src/ApiService/ApiService/Functions/TimerRetention.cs index d4dc9420e..954a5cf9d 100644 --- a/src/ApiService/ApiService/Functions/TimerRetention.cs +++ b/src/ApiService/ApiService/Functions/TimerRetention.cs @@ -54,7 +54,7 @@ public class TimerRetention { select container.Name; foreach (var c in containerNames) { - usedContainers.Add(c); + _ = usedContainers.Add(c); } } diff --git a/src/ApiService/ApiService/Functions/TimerWorkers.cs b/src/ApiService/ApiService/Functions/TimerWorkers.cs index 700760579..76290f7ed 100644 --- a/src/ApiService/ApiService/Functions/TimerWorkers.cs +++ b/src/ApiService/ApiService/Functions/TimerWorkers.cs @@ -18,19 +18,21 @@ public class TimerWorkers { private async Async.Task ProcessScalesets(Service.Scaleset scaleset) { _log.Verbose($"checking scaleset for updates: {scaleset.ScalesetId}"); - await _scaleSetOps.UpdateConfigs(scaleset); + scaleset = await _scaleSetOps.UpdateConfigs(scaleset); var r = await _scaleSetOps.SyncAutoscaleSettings(scaleset); if (!r.IsOk) { _log.Error($"failed to sync auto scale settings due to {r.ErrorV}"); } // if the scaleset is touched during cleanup, don't continue to process it - if (await _scaleSetOps.CleanupNodes(scaleset)) { + var (touched, ss) = await _scaleSetOps.CleanupNodes(scaleset); + if (touched) { _log.Verbose($"scaleset needed cleanup: {scaleset.ScalesetId}"); return; } - await _scaleSetOps.SyncScalesetSize(scaleset); + scaleset = ss; + scaleset = await _scaleSetOps.SyncScalesetSize(scaleset); _ = await _scaleSetOps.ProcessStateUpdate(scaleset); } diff --git a/src/ApiService/ApiService/HttpClient.cs b/src/ApiService/ApiService/HttpClient.cs index 72af2c3ff..89de11da3 100644 --- a/src/ApiService/ApiService/HttpClient.cs +++ b/src/ApiService/ApiService/HttpClient.cs @@ -17,7 +17,7 @@ public class Request { } private async Task Send(HttpMethod method, Uri url, HttpContent? content = null, IDictionary? headers = null) { - var request = new HttpRequestMessage(method: method, requestUri: url); + using var request = new HttpRequestMessage(method: method, requestUri: url); if (_auth is not null) { var (tokenType, accessToken) = await _auth(); diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 94235d6f2..9e88a0f81 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -166,7 +166,10 @@ public class Program { var storageAccount = serviceConfig.OneFuzzFuncStorage; if (storageAccount is not null) { var tableClient = await storage.GetTableServiceClientForAccount(storageAccount); - await Async.Task.WhenAll(toCreate.Select(t => tableClient.CreateTableIfNotExistsAsync(serviceConfig.OneFuzzStoragePrefix + t.Name))); + await Async.Task.WhenAll(toCreate.Select(async t => { + // don't care if it was created or not + _ = await tableClient.CreateTableIfNotExistsAsync(serviceConfig.OneFuzzStoragePrefix + t.Name); + })); } } } diff --git a/src/ApiService/ApiService/onefuzzlib/Config.cs b/src/ApiService/ApiService/onefuzzlib/Config.cs index 67658235a..6f3cd29f4 100644 --- a/src/ApiService/ApiService/onefuzzlib/Config.cs +++ b/src/ApiService/ApiService/onefuzzlib/Config.cs @@ -462,7 +462,7 @@ public class Config : IConfig { var containers = new Dictionary>(); foreach (var container in config.Containers) { - if (exist.Contains(container.Name)) { + if (!exist.Add(container.Name)) { continue; } @@ -470,8 +470,6 @@ public class Config : IConfig { return ResultVoid.Error(new TaskConfigError($"missing container: {container.Name}")); } - _ = exist.Add(container.Name); - if (!containers.ContainsKey(container.Type)) { containers.Add(container.Type, new List()); } diff --git a/src/ApiService/ApiService/onefuzzlib/ConfigOperations.cs b/src/ApiService/ApiService/onefuzzlib/ConfigOperations.cs index 001cc7f0f..4514a412b 100644 --- a/src/ApiService/ApiService/onefuzzlib/ConfigOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ConfigOperations.cs @@ -25,7 +25,7 @@ public class ConfigOperations : Orm, IConfigOperations { private static readonly InstanceConfigCacheKey _key = new(); // singleton key public Task Fetch() => _cache.GetOrCreateAsync(_key, async entry => { - entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); // cached for 10 minutes + entry = entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); // cached for 10 minutes var key = _context.ServiceConfiguration.OneFuzzInstanceName ?? throw new Exception("Environment variable ONEFUZZ_INSTANCE_NAME is not set"); return await GetEntityAsync(key, key); }); @@ -49,9 +49,11 @@ public class ConfigOperations : Orm, IConfigOperations { _log.WithHttpStatus(r.ErrorV).Error($"Failed to replace instance config record"); } } + if (r.IsOk) { - _cache.Set(_key, newConfig); + _ = _cache.Set(_key, newConfig); } + await _context.Events.SendEvent(new EventInstanceConfigUpdated(newConfig)); } } diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs index cf47418d8..ed199a1c6 100644 --- a/src/ApiService/ApiService/onefuzzlib/Containers.cs +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -166,7 +166,7 @@ public class Containers : IContainers { public async Async.Task SaveBlob(Container container, string name, string data, StorageType storageType) { var client = await FindContainer(container, storageType) ?? throw new Exception($"unable to find container: {container} - {storageType}"); - await client.GetBlobClient(name).UploadAsync(new BinaryData(data), overwrite: true); + _ = await client.GetBlobClient(name).UploadAsync(new BinaryData(data), overwrite: true); } public virtual Async.Task GetInstanceId() => _getInstanceId.Value; diff --git a/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs b/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs index 6225bfeca..4fb6f67db 100644 --- a/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs @@ -25,7 +25,7 @@ public class DiskOperations : IDiskOperations { _logTracer.Info($"deleting disks {resourceGroup} : {name}"); var disk = await _creds.GetResourceGroupResource().GetDiskAsync(name); if (disk != null) { - await disk.Value.DeleteAsync(WaitUntil.Started); + _ = await disk.Value.DeleteAsync(WaitUntil.Started); return true; } } catch (Exception e) { diff --git a/src/ApiService/ApiService/onefuzzlib/Extension.cs b/src/ApiService/ApiService/onefuzzlib/Extension.cs index 683b28eb1..d9237104b 100644 --- a/src/ApiService/ApiService/onefuzzlib/Extension.cs +++ b/src/ApiService/ApiService/onefuzzlib/Extension.cs @@ -371,8 +371,8 @@ public class Extensions : IExtensions { public async Task> ReproExtensions(AzureLocation region, Os reproOs, Guid reproId, ReproConfig reproConfig, Container? setupContainer) { // TODO: what about contents of repro.ps1 / repro.sh? var report = await _context.Reports.GetReport(reproConfig.Container, reproConfig.Path); - report.EnsureNotNull($"invalid report: {reproConfig}"); - report?.InputBlob.EnsureNotNull("unable to perform reproduction without an input blob"); + var checkedReport = report.EnsureNotNull($"invalid report: {reproConfig}"); + var inputBlob = checkedReport.InputBlob.EnsureNotNull("unable to perform reproduction without an input blob"); var commands = new List(); if (setupContainer != null) { @@ -395,8 +395,8 @@ public class Extensions : IExtensions { BlobSasPermissions.Read ), await _context.Containers.GetFileSasUrl( - report?.InputBlob?.Container!, - report?.InputBlob?.Name!, + inputBlob.Container, + inputBlob.Name, StorageType.Corpus, BlobSasPermissions.Read ) diff --git a/src/ApiService/ApiService/onefuzzlib/JobOperations.cs b/src/ApiService/ApiService/onefuzzlib/JobOperations.cs index 01391d53d..c9c3c406e 100644 --- a/src/ApiService/ApiService/onefuzzlib/JobOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/JobOperations.cs @@ -78,13 +78,13 @@ public class JobOperations : StatefulOrm, IJobOper Query.EqualEnum("state", JobState.Enabled) }); - var jobs = this.QueryAsync(filter); - - await foreach (var job in jobs) { + await foreach (var job in QueryAsync(filter)) { await foreach (var task in _context.TaskOperations.QueryAsync(Query.PartitionKey(job.JobId.ToString()))) { await _context.TaskOperations.MarkFailed(task, new Error(ErrorCode.TASK_FAILED, new[] { "job never not start" })); } _logTracer.Info($"stopping job that never started: {job.JobId}"); + + // updated result ignored: not used after this loop _ = await _context.JobOperations.Stopping(job); } } diff --git a/src/ApiService/ApiService/onefuzzlib/NodeMessageOperations.cs b/src/ApiService/ApiService/onefuzzlib/NodeMessageOperations.cs index f360cfda2..248744ab5 100644 --- a/src/ApiService/ApiService/onefuzzlib/NodeMessageOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NodeMessageOperations.cs @@ -40,7 +40,7 @@ public class NodeMessageOperations : Orm, INodeMessageOperations { } public async Async.Task SendMessage(Guid machineId, NodeCommand message, string? messageId = null) { - messageId = messageId ?? EntityBase.NewSortedKey; + messageId ??= EntityBase.NewSortedKey; var r = await Insert(new NodeMessage(machineId, messageId, message)); if (!r.IsOk) { _logTracer.WithHttpStatus(r.ErrorV).Error($"failed to insert message with id: {messageId} for machine id: {machineId} message: {message}"); diff --git a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs index 158fcd7a2..26c45543b 100644 --- a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs @@ -54,8 +54,8 @@ public class NotificationOperations : Orm, INotificationOperations await foreach (var (task, containers) in GetQueueTasks()) { if (containers.Contains(container)) { _logTracer.Info($"queuing input {container} {filename} {task.TaskId}"); - var url = _context.Containers.GetFileSasUrl(container, filename, StorageType.Corpus, BlobSasPermissions.Read | BlobSasPermissions.Delete); - await _context.Queue.SendMessage(task.TaskId.ToString(), url?.ToString() ?? "", StorageType.Corpus); + var url = await _context.Containers.GetFileSasUrl(container, filename, StorageType.Corpus, BlobSasPermissions.Read | BlobSasPermissions.Delete); + await _context.Queue.SendMessage(task.TaskId.ToString(), url.ToString(), StorageType.Corpus); } } diff --git a/src/ApiService/ApiService/onefuzzlib/NsgOperations.cs b/src/ApiService/ApiService/onefuzzlib/NsgOperations.cs index a25843eae..568d499fa 100644 --- a/src/ApiService/ApiService/onefuzzlib/NsgOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NsgOperations.cs @@ -84,7 +84,7 @@ namespace Microsoft.OneFuzz.Service { _logTracer.Info($"dissociating nic {nic.Data.Name} with nsg: {_context.Creds.GetBaseResourceGroup()} {nsg.Name}"); nic.Data.NetworkSecurityGroup = null; try { - await _context.Creds.GetResourceGroupResource() + _ = await _context.Creds.GetResourceGroupResource() .GetNetworkInterfaces() .CreateOrUpdateAsync(WaitUntil.Started, nic.Data.Name, nic.Data); } catch (Exception e) { @@ -183,11 +183,10 @@ namespace Microsoft.OneFuzz.Service { } try { - await _context.Creds.GetResourceGroupResource().GetNetworkSecurityGroups().CreateOrUpdateAsync( + _ = await _context.Creds.GetResourceGroupResource().GetNetworkSecurityGroups().CreateOrUpdateAsync( WaitUntil.Started, name, - nsgParams - ); + nsgParams); } catch (RequestFailedException ex) { if (IsConcurrentRequestError(ex.Message)) { // _logTracer.Debug($"create NSG had conflicts with concurrent request, ignoring {ex}"); @@ -291,9 +290,8 @@ namespace Microsoft.OneFuzz.Service { _logTracer.Info($"associating nic {nic.Data.Name} with nsg: {_context.Creds.GetBaseResourceGroup()} {nsg.Data.Name}"); try { - await _context.Creds.GetResourceGroupResource().GetNetworkInterfaces().CreateOrUpdateAsync( - WaitUntil.Started, nic.Data.Name, nic.Data - ); + _ = await _context.Creds.GetResourceGroupResource().GetNetworkInterfaces().CreateOrUpdateAsync( + WaitUntil.Started, nic.Data.Name, nic.Data); } catch (RequestFailedException ex) { if (IsConcurrentRequestError(ex.Message)) { // _logTracer.Debug($"associate NSG with NIC had conflicts with concurrent request, ignoring {ex}"); @@ -312,11 +310,10 @@ namespace Microsoft.OneFuzz.Service { _logTracer.Info($"updating nsg {_context.Creds.GetBaseResourceGroup()}:{nsg.Location}:{nsg.Name}"); try { - await _context.Creds.GetResourceGroupResource().GetNetworkSecurityGroups().CreateOrUpdateAsync( + _ = await _context.Creds.GetResourceGroupResource().GetNetworkSecurityGroups().CreateOrUpdateAsync( WaitUntil.Started, nsg.Name, - nsg - ); + nsg); } catch (RequestFailedException ex) { if (IsConcurrentRequestError(ex.Message)) { //_logTracer.Debug($"create NSG had conflicts with concurrent request, ignoring {ex}"); diff --git a/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs b/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs index 7fe503d15..c59f43dd8 100644 --- a/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs @@ -198,7 +198,8 @@ public class PoolOperations : StatefulOrm, IPoo if (nodes is not null) { await foreach (var node in nodes) { - await _context.NodeOperations.SetShutdown(node); + // ignoring updated result - nodes not returned + _ = await _context.NodeOperations.SetShutdown(node); } } @@ -231,7 +232,8 @@ public class PoolOperations : StatefulOrm, IPoo if (nodes is not null) { await foreach (var node in nodes) { - await _context.NodeOperations.SetHalt(node); + // updated value ignored: 'nodes' is not returned + _ = await _context.NodeOperations.SetHalt(node); } } diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs index b44358e19..fc3bbf4af 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyForwardOperations.cs @@ -54,7 +54,7 @@ public class ProxyForwardOperations : Orm, IProxyForwardOperations var firstEntry = entries.FirstOrDefault(); if (firstEntry != null) { var updated = firstEntry with { EndTime = DateTimeOffset.UtcNow + TimeSpan.FromHours(duration) }; - await this.Update(updated); + await Update(updated).IgnoreResult(); return OneFuzzResult.Ok(updated); } @@ -94,8 +94,8 @@ public class ProxyForwardOperations : Orm, IProxyForwardOperations var regions = new HashSet(); foreach (var entry in entries) { - regions.Add(entry.Region); - await Delete(entry); + _ = regions.Add(entry.Region); + await Delete(entry).IgnoreResult(); } return regions; diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index 59b846181..b58f30cc6 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -41,21 +41,23 @@ public class ProxyOperations : StatefulOrm, IPr } public async Async.Task GetOrCreate(Region region) { - var proxyList = QueryAsync(filter: TableClient.CreateQueryFilter($"region eq {region.String} and outdated eq false")); - - await foreach (var proxy in proxyList) { - if (IsOutdated(proxy)) { - var r1 = await Replace(proxy with { Outdated = true }); - if (!r1.IsOk) { - _logTracer.WithHttpStatus(r1.ErrorV).Error($"failed to replace record to mark proxy {proxy.ProxyId} as outdated"); + { + var proxyList = QueryAsync(filter: TableClient.CreateQueryFilter($"region eq {region.String} and outdated eq false")); + await foreach (var proxy in proxyList) { + if (IsOutdated(proxy)) { + var r1 = await Replace(proxy with { Outdated = true }); + if (!r1.IsOk) { + _logTracer.WithHttpStatus(r1.ErrorV).Error($"failed to replace record to mark proxy {proxy.ProxyId} as outdated"); + } + continue; } - continue; - } - if (!VmStateHelper.Available.Contains(proxy.State)) { - continue; + if (!VmStateHelper.Available.Contains(proxy.State)) { + continue; + } + + return proxy; } - return proxy; } _logTracer.Info($"creating proxy: region:{region}"); @@ -305,7 +307,7 @@ public class ProxyOperations : StatefulOrm, IPr var stoppedVm = await SetState(proxy, VmState.Stopped); _logTracer.Info($"removing proxy: {stoppedVm.Region}"); await _context.Events.SendEvent(new EventProxyDeleted(stoppedVm.Region, stoppedVm.ProxyId)); - await Delete(stoppedVm); + await Delete(stoppedVm).IgnoreResult(); return stoppedVm; } diff --git a/src/ApiService/ApiService/onefuzzlib/Queue.cs b/src/ApiService/ApiService/onefuzzlib/Queue.cs index 8dfb6c5e3..6fab5e0c2 100644 --- a/src/ApiService/ApiService/onefuzzlib/Queue.cs +++ b/src/ApiService/ApiService/onefuzzlib/Queue.cs @@ -44,7 +44,7 @@ public class Queue : IQueue { public async Async.Task SendMessage(string name, string message, StorageType storageType, TimeSpan? visibilityTimeout = null, TimeSpan? timeToLive = null) { var queue = await GetQueueClient(name, storageType); try { - await queue.SendMessageAsync(message, visibilityTimeout: visibilityTimeout, timeToLive: timeToLive); + _ = await queue.SendMessageAsync(message, visibilityTimeout: visibilityTimeout, timeToLive: timeToLive); } catch (Exception ex) { _log.Exception(ex, $"Failed to send message {message}"); throw; diff --git a/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs b/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs index 34dd4e4d2..0d0172a23 100644 --- a/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs @@ -108,6 +108,7 @@ public class ReproOperations : StatefulOrm, IRe public async Async.Task Stopped(Repro repro) { _logTracer.Info($"vm stopped: {repro.VmId}"); + // BUG?: why are we updating repro and then deleting it and returning a new value repro = repro with { State = VmState.Stopped }; var r = await Delete(repro); if (!r.IsOk) { @@ -204,7 +205,7 @@ public class ReproOperations : StatefulOrm, IRe repro = repro with { State = VmState.Running }; } - await Replace(repro); + await Replace(repro).IgnoreResult(); return repro; } diff --git a/src/ApiService/ApiService/onefuzzlib/RequestAccess.cs b/src/ApiService/ApiService/onefuzzlib/RequestAccess.cs index 57e70e0c6..c242f4e8e 100644 --- a/src/ApiService/ApiService/onefuzzlib/RequestAccess.cs +++ b/src/ApiService/ApiService/onefuzzlib/RequestAccess.cs @@ -67,7 +67,7 @@ public class RequestAccess { var segments = path.Split("/", StringSplitOptions.RemoveEmptyEntries); var currentNode = _root; - currentNode.Rules.TryGetValue(method, out var currentRule); + _ = currentNode.Rules.TryGetValue(method, out var currentRule); foreach (var currentSegment in segments) { if (currentNode.Children.TryGetValue(currentSegment, out var node)) { diff --git a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs index 79afa4a1a..d5b626dd1 100644 --- a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs @@ -10,14 +10,14 @@ public interface IScalesetOperations : IStatefulOrm { IAsyncEnumerable SearchByPool(PoolName poolName); - Async.Task UpdateConfigs(Scaleset scaleSet); + Async.Task UpdateConfigs(Scaleset scaleSet); Async.Task> GetById(Guid scalesetId); IAsyncEnumerable GetByObjectId(Guid objectId); - Async.Task CleanupNodes(Scaleset scaleSet); + Async.Task<(bool, Scaleset)> CleanupNodes(Scaleset scaleSet); - Async.Task SyncScalesetSize(Scaleset scaleset); + Async.Task SyncScalesetSize(Scaleset scaleset); Async.Task SetState(Scaleset scaleset, ScalesetState state); public Async.Task> GetNodes(Scaleset scaleset); @@ -56,11 +56,11 @@ public class ScalesetOperations : StatefulOrm SyncScalesetSize(Scaleset scaleset) { // # If our understanding of size is out of sync with Azure, resize the // # scaleset to match our understanding. if (scaleset.State != ScalesetState.Running) { - return; + return scaleset; } var size = await _context.VmssOperations.GetVmssSize(scaleset.ScalesetId); @@ -69,9 +69,10 @@ public class ScalesetOperations : StatefulOrm SyncAutoscaleSettings(Scaleset scaleset) { @@ -221,37 +224,34 @@ public class ScalesetOperations : StatefulOrm UpdateConfigs(Scaleset scaleSet) { if (scaleSet.State == ScalesetState.Halt) { _log.Info($"{SCALESET_LOG_PREFIX} not updating configs, scalest is set to be deleted. scaleset_id: {scaleSet.ScalesetId}"); - return; + return scaleSet; } + if (!scaleSet.NeedsConfigUpdate) { _log.Verbose($"{SCALESET_LOG_PREFIX} config update no needed. scaleset_id: {scaleSet.ScalesetId}"); - return; + return scaleSet; } _log.Info($"{SCALESET_LOG_PREFIX} updating scalset configs. scalset_id: {scaleSet.ScalesetId}"); var pool = await _context.PoolOperations.GetByName(scaleSet.PoolName); - if (!pool.IsOk) { _log.Error($"{SCALESET_LOG_PREFIX} unable to find pool during config update. pool:{scaleSet.PoolName}, scaleset_id:{scaleSet.ScalesetId}"); - await SetFailed(scaleSet, pool.ErrorV); - return; + scaleSet = await SetFailed(scaleSet, pool.ErrorV); + return scaleSet; } var extensions = await _context.Extensions.FuzzExtensions(pool.OkV, scaleSet); var res = await _context.VmssOperations.UpdateExtensions(scaleSet.ScalesetId, extensions); - if (!res.IsOk) { _log.Info($"{SCALESET_LOG_PREFIX} unable to update configs {string.Join(',', res.ErrorV.Errors!)}"); } + + return scaleSet; } public Async.Task SetShutdown(Scaleset scaleset, bool now) @@ -488,21 +488,22 @@ public class ScalesetOperations : StatefulOrm /// /// true if scaleset got modified - public async Async.Task CleanupNodes(Scaleset scaleSet) { + public async Async.Task<(bool, Scaleset)> CleanupNodes(Scaleset scaleSet) { _log.Info($"{SCALESET_LOG_PREFIX} cleaning up nodes. scaleset_id {scaleSet.ScalesetId}"); if (scaleSet.State == ScalesetState.Halt) { _log.Info($"{SCALESET_LOG_PREFIX} halting scaleset scaleset_id {scaleSet.ScalesetId}"); - await Halt(scaleSet); - return true; + scaleSet = await Halt(scaleSet); + return (true, scaleSet); } var pool = await _context.PoolOperations.GetByName(scaleSet.PoolName); if (!pool.IsOk) { _log.Error($"unable to find pool during cleanup {scaleSet.ScalesetId} - {scaleSet.PoolName}"); - await SetFailed(scaleSet, pool.ErrorV!); - return true; + scaleSet = await SetFailed(scaleSet, pool.ErrorV!); + return (true, scaleSet); } + await _context.NodeOperations.ReimageLongLivedNodes(scaleSet.ScalesetId); //ground truth of existing nodes @@ -596,7 +597,7 @@ public class ScalesetOperations : StatefulOrm 0 || toDelete.Count > 0; + return (toReimage.Count > 0 || toDelete.Count > 0, scaleSet); } @@ -627,7 +628,7 @@ public class ScalesetOperations : StatefulOrm machineIds.Contains(node.MachineId)) - .Select(node => _context.NodeOperations.ReleaseScaleInProtection(node))); + .Select(async node => { + await _context.NodeOperations.ReleaseScaleInProtection(node).IgnoreResult(); + })); return; case NodeDisposalStrategy.ScaleIn: @@ -651,7 +654,7 @@ public class ScalesetOperations : StatefulOrm machineIds.Contains(node.MachineId)) .Select(async node => { await _context.NodeOperations.Delete(node); - await _context.NodeOperations.ReleaseScaleInProtection(node); + await _context.NodeOperations.ReleaseScaleInProtection(node).IgnoreResult(); })); } else { _log.Info($"failed to reimage nodes due to {r.ErrorV}"); @@ -680,7 +683,7 @@ public class ScalesetOperations : StatefulOrm machineIds.Contains(node.MachineId)) - .Select(node => _context.NodeOperations.ReleaseScaleInProtection(node))); + .Select(async node => { + await _context.NodeOperations.ReleaseScaleInProtection(node).IgnoreResult(); + })); return; case NodeDisposalStrategy.ScaleIn: @@ -699,7 +704,7 @@ public class ScalesetOperations : StatefulOrm machineIds.Contains(node.MachineId)) .Select(async node => { await _context.NodeOperations.Delete(node); - await _context.NodeOperations.ReleaseScaleInProtection(node); + await _context.NodeOperations.ReleaseScaleInProtection(node).IgnoreResult(); })); return; } @@ -812,10 +817,10 @@ public class ScalesetOperations : StatefulOrm c.Type == ContainerType.Setup) ?? throw new Exception($"task missing setup container: task_type = {task.Config.Task.Type}"); - var setupPs1Exist = _containers.BlobExists(setupContainer.Name, "setup.ps1", StorageType.Corpus); - var setupShExist = _containers.BlobExists(setupContainer.Name, "setup.sh", StorageType.Corpus); - string? setupScript = null; - if (task.Os == Os.Windows && await setupPs1Exist) { - setupScript = "setup.ps1"; + if (task.Os == Os.Windows) { + if (await _containers.BlobExists(setupContainer.Name, "setup.ps1", StorageType.Corpus)) { + setupScript = "setup.ps1"; + } } - if (task.Os == Os.Linux && await setupShExist) { - setupScript = "setup.sh"; + if (task.Os == Os.Linux) { + if (await _containers.BlobExists(setupContainer.Name, "setup.sh", StorageType.Corpus)) { + setupScript = "setup.sh"; + } } var reboot = false; diff --git a/src/ApiService/ApiService/onefuzzlib/Subnet.cs b/src/ApiService/ApiService/onefuzzlib/Subnet.cs index d66e52d58..78cd59741 100644 --- a/src/ApiService/ApiService/onefuzzlib/Subnet.cs +++ b/src/ApiService/ApiService/onefuzzlib/Subnet.cs @@ -51,11 +51,10 @@ public class Subnet : ISubnet { } try { - await _creds.GetResourceGroupResource().GetVirtualNetworks().CreateOrUpdateAsync( + _ = await _creds.GetResourceGroupResource().GetVirtualNetworks().CreateOrUpdateAsync( WaitUntil.Started, name, - virtualNetParam - ); + virtualNetParam); } catch (RequestFailedException ex) { _logTracer.Error($"network creation failed: {name}:{region} {{error}}"); return OneFuzzResultVoid.Error( diff --git a/src/ApiService/ApiService/onefuzzlib/Utils.cs b/src/ApiService/ApiService/onefuzzlib/Utils.cs index 817ba63cf..c1ff87cdb 100644 --- a/src/ApiService/ApiService/onefuzzlib/Utils.cs +++ b/src/ApiService/ApiService/onefuzzlib/Utils.cs @@ -3,8 +3,15 @@ public static class ObjectExtention { public static T EnsureNotNull(this T? thisObject, string message) { if (thisObject == null) { - throw new ArgumentNullException(message); + throw new ArgumentException(message); } + return thisObject; } + + // Explicitly discards the result value. + // In general we should not do this; eventually all call-sites should + // be updated. + public static Async.Task IgnoreResult(this Async.Task task) + => task; } diff --git a/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs b/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs index e0f86cccb..b7e4f5506 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs @@ -19,49 +19,32 @@ namespace Microsoft.OneFuzz.Service { if (Location == null) { // EnsureNotNull does not satisfy the nullability checker throw new ArgumentNullException("Location required for VirtualMachineExtension"); } - TypePropertiesType.EnsureNotNull("TypePropertiesType required for VirtualMachineExtension"); - Publisher.EnsureNotNull("Publisher required for VirtualMachineExtension"); - TypeHandlerVersion.EnsureNotNull("TypeHandlerVersion required for VirtualMachineExtension"); - AutoUpgradeMinorVersion.EnsureNotNull("AutoUpgradeMinorVersion required for VirtualMachineExtension"); - - var settings = Settings ?? new BinaryData(new Dictionary()); - var protectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()); return (Name!, new VirtualMachineExtensionData(Location.Value) { - TypePropertiesType = TypePropertiesType, - Publisher = Publisher, - TypeHandlerVersion = TypeHandlerVersion, - AutoUpgradeMinorVersion = AutoUpgradeMinorVersion, + TypePropertiesType = TypePropertiesType.EnsureNotNull("TypePropertiesType required for VirtualMachineExtension"), + Publisher = Publisher.EnsureNotNull("Publisher required for VirtualMachineExtension"), + TypeHandlerVersion = TypeHandlerVersion.EnsureNotNull("TypeHandlerVersion required for VirtualMachineExtension"), + AutoUpgradeMinorVersion = AutoUpgradeMinorVersion.EnsureNotNull("AutoUpgradeMinorVersion required for VirtualMachineExtension"), EnableAutomaticUpgrade = EnableAutomaticUpgrade, ForceUpdateTag = ForceUpdateTag, - Settings = settings, - ProtectedSettings = protectedSettings + Settings = Settings ?? new BinaryData(new Dictionary()), + ProtectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()), }); } public VirtualMachineScaleSetExtensionData GetAsVirtualMachineScaleSetExtension() { - Name.EnsureNotNull("Name required for VirtualMachineScaleSetExtension"); - TypePropertiesType.EnsureNotNull("TypePropertiesType required for VirtualMachineScaleSetExtension"); - Publisher.EnsureNotNull("Publisher required for VirtualMachineScaleSetExtension"); - TypeHandlerVersion.EnsureNotNull("TypeHandlerVersion required for VirtualMachineScaleSetExtension"); - AutoUpgradeMinorVersion.EnsureNotNull("AutoUpgradeMinorVersion required for VirtualMachineScaleSetExtension"); - - var settings = Settings ?? new BinaryData(new Dictionary()); - var protectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()); - return new VirtualMachineScaleSetExtensionData() { - Name = Name, - TypePropertiesType = TypePropertiesType, - Publisher = Publisher, - TypeHandlerVersion = TypeHandlerVersion, - AutoUpgradeMinorVersion = AutoUpgradeMinorVersion, + Name = Name.EnsureNotNull("Name required for VirtualMachineScaleSetExtension"), + TypePropertiesType = TypePropertiesType.EnsureNotNull("TypePropertiesType required for VirtualMachineScaleSetExtension"), + Publisher = Publisher.EnsureNotNull("Publisher required for VirtualMachineScaleSetExtension"), + TypeHandlerVersion = TypeHandlerVersion.EnsureNotNull("TypeHandlerVersion required for VirtualMachineScaleSetExtension"), + AutoUpgradeMinorVersion = AutoUpgradeMinorVersion.EnsureNotNull("AutoUpgradeMinorVersion required for VirtualMachineScaleSetExtension"), EnableAutomaticUpgrade = EnableAutomaticUpgrade, ForceUpdateTag = ForceUpdateTag, - Settings = settings, - ProtectedSettings = protectedSettings + Settings = Settings ?? new BinaryData(new Dictionary()), + ProtectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()), }; } } } - diff --git a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs index cb1750444..f23cb37f5 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs @@ -100,7 +100,7 @@ public class VmOperations : IVmOperations { if (nic != null) { _logTracer.Info($"deleting nic {resourceGroup}:{name}"); if (nic.Data.NetworkSecurityGroup != null && nsg != null) { - await _context.NsgOperations.DissociateNic(nsg, nic); + _ = await _context.NsgOperations.DissociateNic(nsg, nic); return false; } await _context.IpOperations.DeleteNic(resourceGroup, name); @@ -217,7 +217,7 @@ public class VmOperations : IVmOperations { var vm = await _context.Creds.GetResourceGroupResource().GetVirtualMachineAsync(vmName); try { - await vm.Value.GetVirtualMachineExtensions().CreateOrUpdateAsync( + _ = await vm.Value.GetVirtualMachineExtensions().CreateOrUpdateAsync( WaitUntil.Started, extensionName, extension @@ -318,11 +318,10 @@ public class VmOperations : IVmOperations { } try { - await _context.Creds.GetResourceGroupResource().GetVirtualMachines().CreateOrUpdateAsync( + _ = await _context.Creds.GetResourceGroupResource().GetVirtualMachines().CreateOrUpdateAsync( WaitUntil.Started, name, - vmParams - ); + vmParams); } catch (RequestFailedException ex) { if (ex.ErrorCode == "ResourceNotFound" && ex.Message.Contains("The request failed due to conflict with a concurrent request")) { // _logTracer.Debug($"create VM had conflicts with concurrent request, ignoring {ex.ToString()}"); diff --git a/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs index 8dd887138..c7ca9419f 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs @@ -87,7 +87,7 @@ public class VmssOperations : IVmssOperations { var scalesetResource = GetVmssResource(name); var patch = new VirtualMachineScaleSetPatch(); patch.Sku.Capacity = capacity; - await scalesetResource.UpdateAsync(WaitUntil.Started, patch); + _ = await scalesetResource.UpdateAsync(WaitUntil.Started, patch); return OneFuzzResultVoid.Ok; } else { return OneFuzzResultVoid.Error(canUpdate.ErrorV); @@ -222,7 +222,7 @@ public class VmssOperations : IVmssOperations { instanceVm.Data.ProtectionPolicy.ProtectFromScaleIn = protectFromScaleIn; var vmCollection = GetVmssResource(name).GetVirtualMachineScaleSetVms(); try { - await vmCollection.CreateOrUpdateAsync(WaitUntil.Started, instanceVm.Data.InstanceId, instanceVm.Data); + _ = await vmCollection.CreateOrUpdateAsync(WaitUntil.Started, instanceVm.Data.InstanceId, instanceVm.Data); return OneFuzzResultVoid.Ok; } catch { var msg = $"unable to set protection policy on: {vmId}:{instanceVm.Id}"; @@ -396,7 +396,7 @@ public class VmssOperations : IVmssOperations { public Async.Task> ListAvailableSkus(Region region) => _cache.GetOrCreateAsync>($"compute-skus-{region}", async entry => { - entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); + entry = entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); var sub = _creds.GetSubscriptionResource(); var skus = sub.GetResourceSkusAsync(filter: TableClient.CreateQueryFilter($"location eq {region.String}")); diff --git a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs index 1ea8f82ef..d493ad7b1 100644 --- a/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/WebhookOperations.cs @@ -65,13 +65,14 @@ public class WebhookOperations : Orm, IWebhookOperations { headers["X-Onefuzz-Digest"] = digest; } - var client = new Request(_httpFactory.CreateClient()); + using var httpClient = _httpFactory.CreateClient(); + var client = new Request(httpClient); _logTracer.Info(data); - var response = client.Post(url: webhook.Url, json: data, headers: headers); - var result = response.Result; - if (result.StatusCode == HttpStatusCode.Accepted) { + using var response = await client.Post(url: webhook.Url, json: data, headers: headers); + if (response.StatusCode == HttpStatusCode.Accepted) { return true; } + return false; } diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs b/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs index faa96c4e8..4e3d78fc6 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs @@ -170,13 +170,12 @@ container:{container} filename:{filename}"; public async Async.Task UpdateExisting(WorkItem item, string notificationInfo) { if (_config.OnDuplicate.Comment != null) { var comment = await Render(_config.OnDuplicate.Comment); - await _client.AddCommentAsync( + _ = await _client.AddCommentAsync( new CommentCreate() { Text = comment }, _project, - (int)(item.Id!) - ); + (int)(item.Id!)); } var document = new JsonPatchDocument(); @@ -209,7 +208,7 @@ container:{container} filename:{filename}"; } if (document.Any()) { - await _client.UpdateWorkItemAsync(document, _project, (int)(item.Id!)); + _ = await _client.UpdateWorkItemAsync(document, _project, (int)(item.Id!)); _logTracer.Info($"notify ado: updated work item {item.Id} - {notificationInfo}"); } else { _logTracer.Info($"notify ado: no update for work item {item.Id} - {notificationInfo}"); @@ -228,13 +227,12 @@ container:{container} filename:{filename}"; if (_config.Comment != null) { var comment = await Render(_config.Comment); - await _client.AddCommentAsync( + _ = await _client.AddCommentAsync( new CommentCreate() { Text = comment, }, _project, - (int)(entry.Id!) - ); + (int)(entry.Id!)); } return entry; } diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/GithubIssues.cs b/src/ApiService/ApiService/onefuzzlib/notifications/GithubIssues.cs index 3ae188343..b1d4769f3 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/GithubIssues.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/GithubIssues.cs @@ -114,17 +114,17 @@ public class GithubIssues : NotificationsBase, IGithubIssues { private async Async.Task Update(Issue issue) { _logTracer.Info($"updating issue: {issue}"); if (_config.OnDuplicate.Comment != null) { - await _gh.Issue.Comment.Create(issue.Repository.Id, issue.Number, await Render(_config.OnDuplicate.Comment)); + _ = await _gh.Issue.Comment.Create(issue.Repository.Id, issue.Number, await Render(_config.OnDuplicate.Comment)); } if (_config.OnDuplicate.Labels.Any()) { var labels = await _config.OnDuplicate.Labels.ToAsyncEnumerable() .SelectAwait(async label => await Render(label)) .ToArrayAsync(); - await _gh.Issue.Labels.ReplaceAllForIssue(issue.Repository.Id, issue.Number, labels); + _ = await _gh.Issue.Labels.ReplaceAllForIssue(issue.Repository.Id, issue.Number, labels); } if (_config.OnDuplicate.Reopen && issue.State != ItemState.Open) { - await _gh.Issue.Update(issue.Repository.Id, issue.Number, new IssueUpdate() { + _ = await _gh.Issue.Update(issue.Repository.Id, issue.Number, new IssueUpdate() { State = ItemState.Open }); } @@ -140,7 +140,7 @@ public class GithubIssues : NotificationsBase, IGithubIssues { .SelectAwait(async label => await Render(label)) .ToHashSetAsync(); - labels.Add("OneFuzz"); + _ = labels.Add("OneFuzz"); var newIssue = new NewIssue(await Render(_config.Title)) { Body = await Render(_config.Body), @@ -149,12 +149,10 @@ public class GithubIssues : NotificationsBase, IGithubIssues { labels.ToList().ForEach(label => newIssue.Labels.Add(label)); assignees.ForEach(assignee => newIssue.Assignees.Add(assignee)); - await _gh.Issue.Create( + _ = await _gh.Issue.Create( await Render(_config.Organization), await Render(_config.Repository), - newIssue - ); + newIssue); } } } - diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs b/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs index 8f83cf43e..bf2c819b9 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/NotificationsBase.cs @@ -42,15 +42,25 @@ public abstract class NotificationsBase { private readonly Uri _inputUrl; private readonly Uri _reportUrl; - public static async Async.Task ConstructRenderer(IOnefuzzContext context, Container container, string filename, Report report, Task? task = null, Job? job = null, Uri? targetUrl = null, Uri? inputUrl = null, Uri? reportUrl = null) { + public static async Async.Task ConstructRenderer( + IOnefuzzContext context, + Container container, + string filename, + Report report, + Task? task = null, + Job? job = null, + Uri? targetUrl = null, + Uri? inputUrl = null, + Uri? reportUrl = null) { + task ??= await context.TaskOperations.GetByJobIdAndTaskId(report.JobId, report.TaskId); - task.EnsureNotNull($"invalid task {report.TaskId}"); + var checkedTask = task.EnsureNotNull($"invalid task {report.TaskId}"); job ??= await context.JobOperations.Get(report.JobId); - job.EnsureNotNull($"invalid job {report.JobId}"); + var checkedJob = job.EnsureNotNull($"invalid job {report.JobId}"); if (targetUrl == null) { - var setupContainer = Scheduler.GetSetupContainer(task?.Config!); + var setupContainer = Scheduler.GetSetupContainer(checkedTask.Config); targetUrl = new Uri(context.Containers.AuthDownloadUrl(setupContainer, ReplaceFirstSetup(report.Executable))); } @@ -62,9 +72,25 @@ public abstract class NotificationsBase { inputUrl = new Uri(context.Containers.AuthDownloadUrl(report.InputBlob.Container, report.InputBlob.Name)); } - return new Renderer(container, filename, report, task!, job!, targetUrl, inputUrl!, reportUrl); + return new Renderer( + container, + filename, + report, + checkedTask, + checkedJob, + targetUrl, + inputUrl!, // TODO: incorrect + reportUrl); } - public Renderer(Container container, string filename, Report report, Task task, Job job, Uri targetUrl, Uri inputUrl, Uri reportUrl) { + public Renderer( + Container container, + string filename, + Report report, + Task task, + Job job, + Uri targetUrl, + Uri inputUrl, + Uri reportUrl) { _report = report; _container = container; _filename = filename; diff --git a/src/ApiService/ApiService/onefuzzlib/orm/CustomConverterFactory.cs b/src/ApiService/ApiService/onefuzzlib/orm/CustomConverterFactory.cs index 194b581d9..737b6e287 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/CustomConverterFactory.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/CustomConverterFactory.cs @@ -70,7 +70,7 @@ public sealed class CustomEnumConverter : JsonConverter where T : Enum { return false; } - FormatAndAddToCaches(value, options.Encoder, _skipFormat); + _ = FormatAndAddToCaches(value, options.Encoder, _skipFormat); return true; } } @@ -248,7 +248,7 @@ public sealed class PolymorphicConverter : JsonConverter { var newOptions = new JsonSerializerOptions(k); var thisConverter = newOptions.Converters.FirstOrDefault(c => c.GetType() == typeof(PolymorphicConverterFactory)); if (thisConverter != null) { - newOptions.Converters.Remove(thisConverter); + _ = newOptions.Converters.Remove(thisConverter); } return newOptions; diff --git a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs index 836dc43bb..0d03cc1ac 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs @@ -138,7 +138,7 @@ namespace ApiService.OneFuzzLib.Orm { public interface IStatefulOrm : IOrm where T : StatefulEntityBase where TState : Enum { - Async.Task ProcessStateUpdate(T entity); + Async.Task ProcessStateUpdate(T entity); Async.Task ProcessStateUpdates(T entity, int MaxUpdates = 5); } @@ -201,7 +201,7 @@ namespace ApiService.OneFuzzLib.Orm { /// /// /// - public async Async.Task ProcessStateUpdate(T entity) { + public async Async.Task ProcessStateUpdate(T entity) { TState state = entity.State; var func = GetType().GetMethod(state.ToString()) switch { null => null, @@ -214,9 +214,8 @@ namespace ApiService.OneFuzzLib.Orm { _logTracer.Info($"processing state update: {typeof(T)} - PartitionKey: {partitionKey} RowKey: {rowKey} - {state}"); return await func(entity); } else { - _logTracer.Info($"State function for state: '{state}' not found on type {typeof(T)}"); + throw new ArgumentException($"State function for state: '{state}' not found on type {typeof(T)}"); } - return null; } /// diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index 198f430bd..8ff9fc8b0 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -333,6 +333,12 @@ "resolved": "2.1.0", "contentHash": "1jUT0PwgKO9d9F/X2n762qLp7v/30OpMtJPFRtmjPXUX2/J0lnqiGiSJNNsW3yYTj5StF0Z1yE36TrvtGpcbrg==" }, + "SmartAnalyzers.CSharpExtensions.Annotations": { + "type": "Direct", + "requested": "[4.2.7, )", + "resolved": "4.2.7", + "contentHash": "9fRFxTUwPmH7lukckwEvvKawMcP8ObwnOngN8kx5Bx773WHSku1EGa5BIteV07th5553il76fPX7U1xz2bFmuQ==" + }, "System.IdentityModel.Tokens.Jwt": { "type": "Direct", "requested": "[6.22.1, )", diff --git a/src/ApiService/CSharpExtensions.json b/src/ApiService/CSharpExtensions.json new file mode 100644 index 000000000..bc07b0820 --- /dev/null +++ b/src/ApiService/CSharpExtensions.json @@ -0,0 +1,12 @@ +{ + "$comment1": "See https://github.com/cezarypiatek/CSharpExtensions ", + "$comment2": "These types are all 'builders' and okay to ignore the result of", + "CSE005": { + "IgnoredReturnTypes": [ + "Microsoft.Azure.Functions.Worker.IFunctionsWorkerApplicationBuilder", + "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Moq.Language.Flow.IReturnsResult", + "FluentAssertions.AndConstraint" + ] + } +} diff --git a/src/ApiService/IntegrationTests/ContainersTests.cs b/src/ApiService/IntegrationTests/ContainersTests.cs index 2a6c1dfdf..bf7f9eb44 100644 --- a/src/ApiService/IntegrationTests/ContainersTests.cs +++ b/src/ApiService/IntegrationTests/ContainersTests.cs @@ -50,7 +50,7 @@ public abstract class ContainersTestBase : FunctionTestBase { public async Async.Task CanDelete() { var containerName = Container.Parse("test"); var client = GetContainerClient(containerName); - await client.CreateIfNotExistsAsync(); + _ = await client.CreateIfNotExistsAsync(); var msg = TestHttpRequestData.FromJson("DELETE", new ContainerDelete(containerName)); @@ -89,7 +89,7 @@ public abstract class ContainersTestBase : FunctionTestBase { public async Async.Task CanPost_Existing() { var containerName = Container.Parse("test"); var client = GetContainerClient(containerName); - await client.CreateIfNotExistsAsync(); + _ = await client.CreateIfNotExistsAsync(); var metadata = new Dictionary { { "some", "value" } }; var msg = TestHttpRequestData.FromJson("POST", new ContainerCreate(containerName, metadata)); @@ -113,7 +113,7 @@ public abstract class ContainersTestBase : FunctionTestBase { var containerName = Container.Parse("test"); { var client = GetContainerClient(containerName); - await client.CreateIfNotExistsAsync(); + _ = await client.CreateIfNotExistsAsync(); } var msg = TestHttpRequestData.FromJson("GET", new ContainerGet(containerName)); @@ -143,8 +143,8 @@ public abstract class ContainersTestBase : FunctionTestBase { public async Async.Task List_Existing() { var meta1 = new Dictionary { { "key1", "value1" } }; var meta2 = new Dictionary { { "key2", "value2" } }; - await GetContainerClient(Container.Parse("one")).CreateIfNotExistsAsync(metadata: meta1); - await GetContainerClient(Container.Parse("two")).CreateIfNotExistsAsync(metadata: meta2); + _ = await GetContainerClient(Container.Parse("one")).CreateIfNotExistsAsync(metadata: meta1); + _ = await GetContainerClient(Container.Parse("two")).CreateIfNotExistsAsync(metadata: meta2); var msg = TestHttpRequestData.Empty("GET"); // this means list all @@ -166,12 +166,12 @@ public abstract class ContainersTestBase : FunctionTestBase { private static async Async.Task AssertCanCRUD(Uri sasUrl) { var client = new BlobContainerClient(sasUrl); - await client.UploadBlobAsync("blob", new BinaryData("content")); // create + _ = await client.UploadBlobAsync("blob", new BinaryData("content")); // create var b = Assert.Single(await client.GetBlobsAsync().ToListAsync()); // list using (var s = await client.GetBlobClient(b.Name).OpenReadAsync()) using (var sr = new StreamReader(s)) { Assert.Equal("content", await sr.ReadToEndAsync()); // read } - await client.DeleteBlobAsync("blob"); // delete + using var r = await client.DeleteBlobAsync("blob"); // delete } } diff --git a/src/ApiService/IntegrationTests/DownloadTests.cs b/src/ApiService/IntegrationTests/DownloadTests.cs index a3ee87876..a5c706b56 100644 --- a/src/ApiService/IntegrationTests/DownloadTests.cs +++ b/src/ApiService/IntegrationTests/DownloadTests.cs @@ -74,8 +74,8 @@ public abstract class DownloadTestBase : FunctionTestBase { // set up a file to download var containerName = Container.Parse("xxx"); var container = GetContainerClient(containerName); - await container.CreateAsync(); - await container.UploadBlobAsync("yyy", new BinaryData("content")); + _ = await container.CreateAsync(); + _ = await container.UploadBlobAsync("yyy", new BinaryData("content")); var req = TestHttpRequestData.Empty("GET"); var url = new UriBuilder(req.Url) { Query = "container=xxx&filename=yyy" }.Uri; diff --git a/src/ApiService/IntegrationTests/InfoTests.cs b/src/ApiService/IntegrationTests/InfoTests.cs index 37e5c036e..261e2c8f6 100644 --- a/src/ApiService/IntegrationTests/InfoTests.cs +++ b/src/ApiService/IntegrationTests/InfoTests.cs @@ -50,8 +50,8 @@ public abstract class InfoTestBase : FunctionTestBase { var instanceId = Guid.NewGuid().ToString(); var baseConfigContainer = WellKnownContainers.BaseConfig; var containerClient = GetContainerClient(baseConfigContainer); - await containerClient.CreateAsync(); - await containerClient.GetBlobClient("instance_id").UploadAsync(new BinaryData(instanceId)); + _ = await containerClient.CreateAsync(); + _ = await containerClient.GetBlobClient("instance_id").UploadAsync(new BinaryData(instanceId)); var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context); var func = new Info(auth, Context); diff --git a/src/ApiService/IntegrationTests/IntegrationTests.csproj b/src/ApiService/IntegrationTests/IntegrationTests.csproj index ac62f4aff..59b51a944 100644 --- a/src/ApiService/IntegrationTests/IntegrationTests.csproj +++ b/src/ApiService/IntegrationTests/IntegrationTests.csproj @@ -23,4 +23,7 @@ + + + diff --git a/src/ApiService/IntegrationTests/_FunctionTestBase.cs b/src/ApiService/IntegrationTests/_FunctionTestBase.cs index d94903360..fceadd81e 100644 --- a/src/ApiService/IntegrationTests/_FunctionTestBase.cs +++ b/src/ApiService/IntegrationTests/_FunctionTestBase.cs @@ -70,7 +70,7 @@ public abstract class FunctionTestBase : IAsyncLifetime { } protected static string BodyAsString(HttpResponseData data) { - data.Body.Seek(0, SeekOrigin.Begin); + _ = data.Body.Seek(0, SeekOrigin.Begin); using var sr = new StreamReader(data.Body); return sr.ReadToEnd(); } @@ -85,7 +85,7 @@ public abstract class FunctionTestBase : IAsyncLifetime { .Where(c => c.IsDeleted != true) .Select(async container => { try { - await blobClient.DeleteBlobContainerAsync(container.Name); + using var _ = await blobClient.DeleteBlobContainerAsync(container.Name); Logger.Info($"cleaned up container {container.Name}"); } catch (Exception ex) { // swallow any exceptions: this is a best-effort attempt to cleanup @@ -100,7 +100,7 @@ public abstract class FunctionTestBase : IAsyncLifetime { .QueryAsync(filter: Query.StartsWith("TableName", _storagePrefix)) .Select(async table => { try { - await tableClient.DeleteTableAsync(table.Name); + using var _ = await tableClient.DeleteTableAsync(table.Name); Logger.Info($"cleaned up table {table.Name}"); } catch (Exception ex) { // swallow any exceptions: this is a best-effort attempt to cleanup diff --git a/src/ApiService/IntegrationTests/packages.lock.json b/src/ApiService/IntegrationTests/packages.lock.json index 3d843d602..c7136ac10 100644 --- a/src/ApiService/IntegrationTests/packages.lock.json +++ b/src/ApiService/IntegrationTests/packages.lock.json @@ -1185,6 +1185,11 @@ "resolved": "2.1.0", "contentHash": "1jUT0PwgKO9d9F/X2n762qLp7v/30OpMtJPFRtmjPXUX2/J0lnqiGiSJNNsW3yYTj5StF0Z1yE36TrvtGpcbrg==" }, + "SmartAnalyzers.CSharpExtensions.Annotations": { + "type": "Transitive", + "resolved": "4.2.7", + "contentHash": "9fRFxTUwPmH7lukckwEvvKawMcP8ObwnOngN8kx5Bx773WHSku1EGa5BIteV07th5553il76fPX7U1xz2bFmuQ==" + }, "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", @@ -2309,6 +2314,7 @@ "Octokit": "2.0.1", "Scriban": "5.5.0", "Semver": "2.1.0", + "SmartAnalyzers.CSharpExtensions.Annotations": "4.2.7", "System.IdentityModel.Tokens.Jwt": "6.22.1", "System.Linq.Async": "6.0.1", "TaskTupleAwaiter": "2.0.0" diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index 0c8de3617..919c74bd0 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -691,7 +691,7 @@ namespace Tests { ITestOutputHelper _output; public OrmModelsTest(ITestOutputHelper output) { - Arb.Register(); + _ = Arb.Register(); _output = output; } @@ -773,7 +773,7 @@ namespace Tests { ITestOutputHelper _output; public OrmJsonSerialization(ITestOutputHelper output) { - Arb.Register(); + _ = Arb.Register(); _output = output; } diff --git a/src/ApiService/Tests/OrmTest.cs b/src/ApiService/Tests/OrmTest.cs index 48b538c52..0724b8977 100644 --- a/src/ApiService/Tests/OrmTest.cs +++ b/src/ApiService/Tests/OrmTest.cs @@ -141,10 +141,10 @@ namespace Tests { var json = JsonNode.Parse(tableEntity.GetString("the_object"))?.AsObject() ?? throw new InvalidOperationException("Could not parse objec"); - json.TryGetPropertyValue("the_name", out var theName); - json.TryGetPropertyValue("the_enum", out var theEnum); - json.TryGetPropertyValue("the_flag", out var theFlag); - json.TryGetPropertyValue("the_enum_value", out var theEnumValue); + Assert.True(json.TryGetPropertyValue("the_name", out var theName)); + Assert.True(json.TryGetPropertyValue("the_enum", out var theEnum)); + Assert.True(json.TryGetPropertyValue("the_flag", out var theFlag)); + Assert.True(json.TryGetPropertyValue("the_enum_value", out var theEnumValue)); Assert.Equal(entity1.TheObject.TheName, theName?.GetValue()); Assert.Equal("the_two", theEnum?.GetValue()); diff --git a/src/ApiService/Tests/RequestAccessTests.cs b/src/ApiService/Tests/RequestAccessTests.cs index be0335980..909efc6dd 100644 --- a/src/ApiService/Tests/RequestAccessTests.cs +++ b/src/ApiService/Tests/RequestAccessTests.cs @@ -59,9 +59,9 @@ public class RequestAccessTests { [Fact] public void TestAddingRuleOnSamePath() { - Assert.Throws(() => { + _ = Assert.Throws(() => { var guid1 = Guid.NewGuid(); - RequestAccess.Build( + _ = RequestAccess.Build( new Dictionary{ { "a/b/c", new ApiAccessRule( Methods: new[]{"get"}, diff --git a/src/ApiService/Tests/SchedulerTests.cs b/src/ApiService/Tests/SchedulerTests.cs index de8058d8c..00e6021ca 100644 --- a/src/ApiService/Tests/SchedulerTests.cs +++ b/src/ApiService/Tests/SchedulerTests.cs @@ -129,7 +129,7 @@ public class SchedulerTests { Assert.Equal(buckets.Count, bucketCount); foreach (var task in tasks) { - Assert.Single(buckets, b => b.Contains(task)); + _ = Assert.Single(buckets, b => b.Contains(task)); } } } diff --git a/src/ApiService/Tests/Tests.csproj b/src/ApiService/Tests/Tests.csproj index 426e41944..81262f441 100644 --- a/src/ApiService/Tests/Tests.csproj +++ b/src/ApiService/Tests/Tests.csproj @@ -25,4 +25,7 @@ + + + diff --git a/src/ApiService/Tests/packages.lock.json b/src/ApiService/Tests/packages.lock.json index 13dcea0c2..b7366745e 100644 --- a/src/ApiService/Tests/packages.lock.json +++ b/src/ApiService/Tests/packages.lock.json @@ -1234,6 +1234,11 @@ "resolved": "2.1.0", "contentHash": "1jUT0PwgKO9d9F/X2n762qLp7v/30OpMtJPFRtmjPXUX2/J0lnqiGiSJNNsW3yYTj5StF0Z1yE36TrvtGpcbrg==" }, + "SmartAnalyzers.CSharpExtensions.Annotations": { + "type": "Transitive", + "resolved": "4.2.7", + "contentHash": "9fRFxTUwPmH7lukckwEvvKawMcP8ObwnOngN8kx5Bx773WHSku1EGa5BIteV07th5553il76fPX7U1xz2bFmuQ==" + }, "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", @@ -2436,6 +2441,7 @@ "Octokit": "2.0.1", "Scriban": "5.5.0", "Semver": "2.1.0", + "SmartAnalyzers.CSharpExtensions.Annotations": "4.2.7", "System.IdentityModel.Tokens.Jwt": "6.22.1", "System.Linq.Async": "6.0.1", "TaskTupleAwaiter": "2.0.0"