From 63130a63f17508d167d0134e71e175753d8caa7c Mon Sep 17 00:00:00 2001 From: stas Date: Wed, 31 Aug 2022 08:41:13 -0700 Subject: [PATCH 1/5] more tests --- src/ApiService/ApiService/Functions/Proxy.cs | 2 +- .../ApiService/onefuzzlib/VmOperations.cs | 3 ++- .../FunctionalTests/1f-api/ApiBase.cs | 6 ++--- .../FunctionalTests/1f-api/Error.cs | 12 ++++----- .../FunctionalTests/1f-api/Helpers.cs | 26 +++++++++++++++++++ src/ApiService/FunctionalTests/1f-api/Node.cs | 4 +-- src/ApiService/FunctionalTests/1f-api/Pool.cs | 6 ++--- .../FunctionalTests/1f-api/Proxy.cs | 26 +++++++++++++------ .../FunctionalTests/1f-api/Scaleset.cs | 26 ++++++++++--------- .../FunctionalTests/GlobalSuppressions.cs | 3 +++ src/ApiService/FunctionalTests/TestNode.cs | 18 +++++++++++++ src/ApiService/FunctionalTests/TestProxy.cs | 17 +++--------- .../FunctionalTests/TestScaleset.cs | 24 +++++++---------- 13 files changed, 108 insertions(+), 65 deletions(-) create mode 100644 src/ApiService/FunctionalTests/1f-api/Helpers.cs diff --git a/src/ApiService/ApiService/Functions/Proxy.cs b/src/ApiService/ApiService/Functions/Proxy.cs index 29013507a2..74959b12c8 100644 --- a/src/ApiService/ApiService/Functions/Proxy.cs +++ b/src/ApiService/ApiService/Functions/Proxy.cs @@ -149,7 +149,7 @@ private async Async.Task Delete(HttpRequestData req) { return await _context.RequestHandling.NotOk( req, request.ErrorV, - "debug_proxy delet"); + "debug_proxy delete"); } var regions = await _context.ProxyForwardOperations.RemoveForward( diff --git a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs index 2f2509dfab..22a714de74 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs @@ -204,7 +204,8 @@ public async Task Create(Vm vm) { return (await vm.Value.GetVirtualMachineExtensionAsync(extensionName)).Value.Data; } catch (RequestFailedException ex) { - _logTracer.Info($"extension does not exist {ex}"); + _logTracer.Exception(ex, $"extension {extensionName} does not exist"); + _logTracer.Info($"extension {extensionName} does not exist {ex}"); return null; } } diff --git a/src/ApiService/FunctionalTests/1f-api/ApiBase.cs b/src/ApiService/FunctionalTests/1f-api/ApiBase.cs index 5a8531d93a..5c32551bad 100644 --- a/src/ApiService/FunctionalTests/1f-api/ApiBase.cs +++ b/src/ApiService/FunctionalTests/1f-api/ApiBase.cs @@ -5,11 +5,11 @@ namespace FunctionalTests; -interface IFromJsonElement { +public interface IFromJsonElement { T Convert(JsonElement e); } -class BooleanResult : IFromJsonElement { +public class BooleanResult : IFromJsonElement { JsonElement _e; public BooleanResult() { } public BooleanResult(JsonElement e) => _e = e; @@ -19,7 +19,7 @@ public BooleanResult() { } public BooleanResult Convert(JsonElement e) => new BooleanResult(e); } -abstract class ApiBase { +public abstract class ApiBase { Uri _endpoint; Microsoft.OneFuzz.Service.Request _request; diff --git a/src/ApiService/FunctionalTests/1f-api/Error.cs b/src/ApiService/FunctionalTests/1f-api/Error.cs index 9fb5f22643..be2a826029 100644 --- a/src/ApiService/FunctionalTests/1f-api/Error.cs +++ b/src/ApiService/FunctionalTests/1f-api/Error.cs @@ -3,7 +3,7 @@ namespace FunctionalTests; -class Error : IComparable, IFromJsonElement { +public class Error : IComparable, IFromJsonElement { JsonElement _e; public Error(JsonElement e) { @@ -21,18 +21,18 @@ public static bool IsError(JsonElement res) { return res.ValueKind == JsonValueKind.Object && res.TryGetProperty("code", out _) && res.TryGetProperty("errors", out _); } - public int CompareTo(Error? error) { - if (error is null) { + public int CompareTo(Error? other) { + if (other is null) { return -1; } - var sameErrorMessages = Errors.Count() == error.Errors.Count(); - foreach (var s in error.Errors) { + var sameErrorMessages = Errors.Count() == other.Errors.Count(); + foreach (var s in other.Errors) { if (!sameErrorMessages) break; sameErrorMessages = Errors.Contains(s); } - if (error.Code == this.Code && sameErrorMessages) { + if (other.Code == this.Code && sameErrorMessages) { return 0; } else return 1; diff --git a/src/ApiService/FunctionalTests/1f-api/Helpers.cs b/src/ApiService/FunctionalTests/1f-api/Helpers.cs new file mode 100644 index 0000000000..eae7ce6ede --- /dev/null +++ b/src/ApiService/FunctionalTests/1f-api/Helpers.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace FunctionalTests { + public class Helpers { + + public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os, string? region = null, int numNodes = 2 ) { + var newPoolId = Guid.NewGuid().ToString(); + var newPoolName = PoolApi.TestPoolPrefix + newPoolId; + var newPool = await poolApi.Create(newPoolName, os); + + Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}"); + var newScalesetResult = await scalesetApi.Create(newPool.OkV!.Name, numNodes, region: region); + + Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}"); + var newScaleset = newScalesetResult.OkV!; + + return (newPool.OkV!, newScaleset); + + } + } +} diff --git a/src/ApiService/FunctionalTests/1f-api/Node.cs b/src/ApiService/FunctionalTests/1f-api/Node.cs index cba877fa02..bd4be3a569 100644 --- a/src/ApiService/FunctionalTests/1f-api/Node.cs +++ b/src/ApiService/FunctionalTests/1f-api/Node.cs @@ -5,7 +5,7 @@ namespace FunctionalTests; -class Node : IFromJsonElement { +public class Node : IFromJsonElement { JsonElement _e; public Node() { } @@ -35,7 +35,7 @@ public Node() { } } -class NodeApi : ApiBase { +public class NodeApi : ApiBase { public NodeApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) : base(endpoint, "/api/Node", request, output) { diff --git a/src/ApiService/FunctionalTests/1f-api/Pool.cs b/src/ApiService/FunctionalTests/1f-api/Pool.cs index c9062c7785..5abb7f9ae0 100644 --- a/src/ApiService/FunctionalTests/1f-api/Pool.cs +++ b/src/ApiService/FunctionalTests/1f-api/Pool.cs @@ -5,7 +5,7 @@ namespace FunctionalTests; -class Pool : IFromJsonElement { +public class Pool : IFromJsonElement { JsonElement _e; @@ -19,9 +19,9 @@ public Pool() { } public Pool Convert(JsonElement e) => new Pool(e); } -class PoolApi : ApiBase { +public class PoolApi : ApiBase { - public static string TestPoolPrefix = "FT-DELETE-"; + public const string TestPoolPrefix = "FT-DELETE-"; public PoolApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) : base(endpoint, "/api/Pool", request, output) { diff --git a/src/ApiService/FunctionalTests/1f-api/Proxy.cs b/src/ApiService/FunctionalTests/1f-api/Proxy.cs index c4a5125578..340c4a72dd 100644 --- a/src/ApiService/FunctionalTests/1f-api/Proxy.cs +++ b/src/ApiService/FunctionalTests/1f-api/Proxy.cs @@ -3,9 +3,7 @@ using Xunit.Abstractions; namespace FunctionalTests; - - -class Proxy : IFromJsonElement { +public class Proxy : IFromJsonElement { JsonElement _e; public Proxy() { } @@ -19,8 +17,7 @@ public Proxy() { } public Proxy Convert(JsonElement e) => new Proxy(e); } - -class Forward : IFromJsonElement, IComparable { +public class Forward : IFromJsonElement, IComparable { JsonElement _e; public Forward() { } public Forward(JsonElement e) => _e = e; @@ -43,14 +40,27 @@ public int CompareTo(Forward? other) { } } -class ProxyGetResult : IFromJsonElement, IComparable { +public class ProxyGetResult : IFromJsonElement, IComparable { JsonElement _e; public ProxyGetResult() { } public ProxyGetResult(JsonElement e) => _e = e; - public string? Ip => _e.ValueKind == JsonValueKind.Null ? null : _e.GetProperty("ip").GetString(); + public string? Ip { + get { + JsonElement ip; + if (_e.TryGetProperty("ip", out ip)){ + if (ip.ValueKind == JsonValueKind.Null) { + return null; + } else { + return ip.GetString(); + } + } else { + return null; + } + } + } public Forward Forward => new Forward(_e.GetProperty("forward")); @@ -76,7 +86,7 @@ public int CompareTo(ProxyGetResult? other) { } -class ProxyApi : ApiBase { +public class ProxyApi : ApiBase { public ProxyApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) : base(endpoint, "/api/proxy", request, output) { diff --git a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs index cd95482310..4f72e683c5 100644 --- a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs +++ b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs @@ -4,10 +4,7 @@ using Xunit.Abstractions; namespace FunctionalTests; - - - -class Authentication { +public class Authentication { JsonElement _e; public Authentication() { } @@ -20,7 +17,7 @@ public Authentication() { } } -class ScalesetNodeState { +public class ScalesetNodeState { JsonElement _e; public ScalesetNodeState() { } @@ -32,7 +29,7 @@ public ScalesetNodeState() { } public string? NodeState => _e.GetProperty("state").GetString(); } -class Scaleset : IFromJsonElement { +public class Scaleset : IFromJsonElement { JsonElement _e; public Scaleset() { } public Scaleset(JsonElement e) => _e = e; @@ -68,7 +65,7 @@ public Scaleset() { } public Scaleset Convert(JsonElement e) => new Scaleset(e); } -class ScalesetApi : ApiBase { +public class ScalesetApi : ApiBase { public const string Image_Ubuntu_20_04 = "Canonical:0001-com-ubuntu-server-focal:20_04-lts:latest"; @@ -78,20 +75,25 @@ public ScalesetApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITes public async Task, Error>> Get(Guid? id = null, string? state = null, bool? includeAuth = false) { var root = new JsonObject(); - root.Add("scaleset_id", id); - root.Add("state", state); - root.Add("include_auth", includeAuth); + if (id is not null) + root.Add("scaleset_id", id); + if (state is not null) + root.Add("state", state); + if (includeAuth is not null) + root.Add("include_auth", includeAuth); var res = await Get(root); return IEnumerableResult(res); } - public async Task> Create(string poolName, int size, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { + public async Task> Create(string poolName, int size, string? region=null, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { var rootScalesetCreate = new JsonObject(); rootScalesetCreate.Add("pool_name", poolName); rootScalesetCreate.Add("vm_sku", vmSku); rootScalesetCreate.Add("image", image); rootScalesetCreate.Add("size", size); - rootScalesetCreate.Add("spot_instance", spotInstance); + rootScalesetCreate.Add("spot_instances", spotInstance); + if (region is not null) + rootScalesetCreate.Add("region", region); var tags = new JsonObject(); tags.Add("Purpose", "Functional-Test"); diff --git a/src/ApiService/FunctionalTests/GlobalSuppressions.cs b/src/ApiService/FunctionalTests/GlobalSuppressions.cs index 537cde53b7..6425018ac8 100644 --- a/src/ApiService/FunctionalTests/GlobalSuppressions.cs +++ b/src/ApiService/FunctionalTests/GlobalSuppressions.cs @@ -6,3 +6,6 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Style", "IDE0005:Using directive is unnecessary.", Justification = "Test code")] +[assembly: SuppressMessage("Design", "CA1036:Override methods on comparable types", Justification = "Test code", Scope = "type", Target = "~T:FunctionalTests.Error")] +[assembly: SuppressMessage("Design", "CA1036:Override methods on comparable types", Justification = "Test code", Scope = "type", Target = "~T:FunctionalTests.Forward")] +[assembly: SuppressMessage("Design", "CA1036:Override methods on comparable types", Justification = "Test code", Scope = "type", Target = "~T:FunctionalTests.ProxyGetResult")] diff --git a/src/ApiService/FunctionalTests/TestNode.cs b/src/ApiService/FunctionalTests/TestNode.cs index 0994249b16..cbf61b523a 100644 --- a/src/ApiService/FunctionalTests/TestNode.cs +++ b/src/ApiService/FunctionalTests/TestNode.cs @@ -8,11 +8,15 @@ namespace FunctionalTests { public class TestNode { NodeApi _nodeApi; + ScalesetApi _scalesetApi; + PoolApi _poolApi; private readonly ITestOutputHelper _output; public TestNode(ITestOutputHelper output) { _output = output; _nodeApi = new NodeApi(ApiClient.Endpoint, ApiClient.Request, output); + _scalesetApi = new ScalesetApi(ApiClient.Endpoint, ApiClient.Request, output); + _poolApi = new PoolApi(ApiClient.Endpoint, ApiClient.Request, output); } [Fact] @@ -34,5 +38,19 @@ async Task GetAllNodes() { } + [Fact] + async Task GetPatchPostDelete() { + + var (pool, scaleset) = await Helpers.CreatePoolAndScaleset(_poolApi, _scalesetApi, "linux"); + + scaleset = await _scalesetApi.WaitWhile(scaleset.ScalesetId, sc => sc.State == "init" || sc.State == "setup"); + Assert.True(scaleset.Nodes!.Count > 0); + + + + + } + + } } diff --git a/src/ApiService/FunctionalTests/TestProxy.cs b/src/ApiService/FunctionalTests/TestProxy.cs index a2056a20f1..e24a6f7ce2 100644 --- a/src/ApiService/FunctionalTests/TestProxy.cs +++ b/src/ApiService/FunctionalTests/TestProxy.cs @@ -37,16 +37,7 @@ public async Task GetProxies() { [Fact] public async Task CreateResetDelete() { - var newPoolId = Guid.NewGuid().ToString(); - var newPoolName = PoolApi.TestPoolPrefix + newPoolId; - var newPool = await _poolApi.Create(newPoolName, "linux"); - - Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}"); - - var newScalesetResult = await _scalesetApi.Create(newPool.OkV!.Name, 2); - - Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}"); - var newScaleset = newScalesetResult.OkV!; + var (newPool, newScaleset) = await Helpers.CreatePoolAndScaleset(_poolApi, _scalesetApi, "linux"); newScaleset = await _scalesetApi.WaitWhile(newScaleset.ScalesetId, sc => sc.State == "init" || sc.State == "setup"); @@ -81,12 +72,10 @@ public async Task CreateResetDelete() { _output.WriteLine($"deleted proxy"); - var deletePool = await _poolApi.Delete(newPoolName); + var deletePool = await _poolApi.Delete(newPool.Name); Assert.True(deletePool.Result); - _output.WriteLine($"deleted pool {newPoolName}"); + _output.WriteLine($"deleted pool {newPool.Name}"); } - - } } diff --git a/src/ApiService/FunctionalTests/TestScaleset.cs b/src/ApiService/FunctionalTests/TestScaleset.cs index 4f3cf3b0d5..644cd04cfd 100644 --- a/src/ApiService/FunctionalTests/TestScaleset.cs +++ b/src/ApiService/FunctionalTests/TestScaleset.cs @@ -36,16 +36,10 @@ public async Task GetScalesets() { [Fact] public async Task CreateAndDelete() { - var newPoolId = Guid.NewGuid().ToString(); - var newPoolName = PoolApi.TestPoolPrefix + newPoolId; - var newPool = await _poolApi.Create(newPoolName, "linux"); + var (newPool, newScaleset) = await Helpers.CreatePoolAndScaleset(_poolApi, _scalesetApi, "linux"); - Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}"); - - var newScalesetResult = await _scalesetApi.Create(newPool.OkV!.Name, 2); - - Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}"); - var newScaleset = newScalesetResult.OkV!; + var newScalesetResultAgain = await _scalesetApi.Create(newPool.Name, 2); + var newScalesetResultAgainAgain = await _scalesetApi.Create(newPool.Name, 5); try { _output.WriteLine($"New scale set info id: {newScaleset.ScalesetId}, pool: {newScaleset.PoolName}, state: {newScaleset.State}, error: {newScaleset.Error}"); @@ -56,7 +50,7 @@ public async Task CreateAndDelete() { var poolsCreated = await _poolApi.Get(); Assert.True(poolsCreated.IsOk, $"failed to get pools: {poolsCreated.ErrorV}"); - var newPools = poolsCreated.OkV!.Where(p => p.Name == newPoolName); + var newPools = poolsCreated.OkV!.Where(p => p.Name == newPool.Name); var newScalesets = scalesetsCreated.OkV!.Where(sc => sc.ScalesetId == newScaleset.ScalesetId); Assert.True(newPools.Count() == 1); @@ -94,25 +88,25 @@ public async Task CreateAndDelete() { } } finally { var preDeleteScalesets = await _scalesetApi.Get(); - var deletedPoolResult = await _poolApi.Delete(newPoolName); + var deletedPoolResult = await _poolApi.Delete(newPool.Name); Assert.True(preDeleteScalesets.IsOk, $"failed to get pre-deleted scalesets due to: {preDeleteScalesets.ErrorV}"); - var preDelete = preDeleteScalesets.OkV!.Where(sc => sc.PoolName == newPoolName); + var preDelete = preDeleteScalesets.OkV!.Where(sc => sc.PoolName == newPool.Name); Assert.True(preDelete.Count() == 1); Result, Error> deletedPool; do { await Task.Delay(TimeSpan.FromSeconds(10.0)); - deletedPool = await _poolApi.Get(newPoolName); + deletedPool = await _poolApi.Get(newPool.Name); } while (deletedPool.IsOk); Assert.True(deletedPool.ErrorV!.UnableToFindPoolError); var postDeleteScalesets = await _scalesetApi.Get(); Assert.True(postDeleteScalesets.IsOk, $"failed to get scalesets after finishing pool deletion due to {postDeleteScalesets.ErrorV}"); - _output.WriteLine($"Pool is deleted {newPoolName}"); + _output.WriteLine($"Pool is deleted {newPool.Name}"); - var postDelete = postDeleteScalesets.OkV!.Where(sc => sc.PoolName == newPoolName); + var postDelete = postDeleteScalesets.OkV!.Where(sc => sc.PoolName == newPool.Name); Assert.False(postDelete.Any()); var patch1 = await _scalesetApi.Patch(newScaleset.ScalesetId, 1); Assert.False(patch1.IsOk); From da0423fb56d11fd0cafc97b8455d2319f636ec1e Mon Sep 17 00:00:00 2001 From: stas Date: Wed, 31 Aug 2022 12:30:50 -0700 Subject: [PATCH 2/5] revert compute, since "released" version has bug when creating scaleset extensions --- src/ApiService/ApiService/ApiService.csproj | 2 +- .../ApiService/Functions/TimerProxy.cs | 14 +++++++----- .../ApiService/onefuzzlib/DiskOperations.cs | 8 +++---- .../ApiService/onefuzzlib/ImageOperations.cs | 18 +++++++-------- .../ApiService/onefuzzlib/IpOperations.cs | 14 +++++++++--- .../ApiService/onefuzzlib/Network.cs | 2 +- .../ApiService/onefuzzlib/ProxyOperations.cs | 2 +- .../onefuzzlib/ScalesetOperations.cs | 2 +- .../ApiService/onefuzzlib/Subnet.cs | 13 +++++++---- .../onefuzzlib/VmExtensionWrapper.cs | 7 ++++-- .../ApiService/onefuzzlib/VmOperations.cs | 20 ++++++++--------- .../ApiService/onefuzzlib/VmssOperations.cs | 22 +++++++++---------- src/ApiService/ApiService/packages.lock.json | 10 ++++----- .../FunctionalTests/1f-api/Helpers.cs | 6 +++-- src/ApiService/FunctionalTests/1f-api/Node.cs | 8 +++---- src/ApiService/FunctionalTests/1f-api/Pool.cs | 14 ++++++++---- .../FunctionalTests/1f-api/Proxy.cs | 9 +++++--- .../FunctionalTests/1f-api/Scaleset.cs | 2 ++ src/ApiService/FunctionalTests/TestNode.cs | 15 ++++++++++++- src/ApiService/FunctionalTests/TestPool.cs | 2 +- .../FunctionalTests/TestScaleset.cs | 17 +++++++++++--- .../IntegrationTests/packages.lock.json | 10 ++++----- src/ApiService/Tests/packages.lock.json | 10 ++++----- 23 files changed, 139 insertions(+), 88 deletions(-) diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index 26b8ddadd0..f2502368d0 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/ApiService/ApiService/Functions/TimerProxy.cs b/src/ApiService/ApiService/Functions/TimerProxy.cs index 024f8a0b73..e5e89ba2f5 100644 --- a/src/ApiService/ApiService/Functions/TimerProxy.cs +++ b/src/ApiService/ApiService/Functions/TimerProxy.cs @@ -64,14 +64,16 @@ public async Async.Task Run([TimerTrigger("00:00:30")] TimerInfo myTimer) { // since we do not support bring your own NSG if (await nsgOpertions.GetNsg(region) != null) { - var network = await Network.Create(region, _context); + var network = await Network.Init(region, _context); var subnet = await network.GetSubnet(); - var vnet = await network.GetVnet(); - if (subnet != null && vnet != null) { - var result = await nsgOpertions.AssociateSubnet(region, vnet, subnet); - if (!result.OkV) { - _logger.Error($"Failed to associate NSG and subnet due to {result.ErrorV} in region {region}"); + if (subnet != null) { + var vnet = await network.GetVnet(); + if (vnet != null) { + var result = await nsgOpertions.AssociateSubnet(region, vnet, subnet); + if (!result.OkV) { + _logger.Error($"Failed to associate NSG and subnet due to {result.ErrorV} in region {region}"); + } } } } diff --git a/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs b/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs index 7e0268947f..6225bfeca4 100644 --- a/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/DiskOperations.cs @@ -5,7 +5,7 @@ namespace Microsoft.OneFuzz.Service; public interface IDiskOperations { - DiskImageCollection ListDisks(string resourceGroup); + DiskCollection ListDisks(string resourceGroup); Async.Task DeleteDisk(string resourceGroup, string name); } @@ -23,7 +23,7 @@ public DiskOperations(ILogTracer log, ICreds creds) { public async Task DeleteDisk(string resourceGroup, string name) { try { _logTracer.Info($"deleting disks {resourceGroup} : {name}"); - var disk = await _creds.GetResourceGroupResource().GetDiskImageAsync(name); + var disk = await _creds.GetResourceGroupResource().GetDiskAsync(name); if (disk != null) { await disk.Value.DeleteAsync(WaitUntil.Started); return true; @@ -35,8 +35,8 @@ public async Task DeleteDisk(string resourceGroup, string name) { return false; } - public DiskImageCollection ListDisks(string resourceGroup) { + public DiskCollection ListDisks(string resourceGroup) { _logTracer.Info($"listing disks {resourceGroup}"); - return _creds.GetResourceGroupResource().GetDiskImages(); + return _creds.GetResourceGroupResource().GetDisks(); } } diff --git a/src/ApiService/ApiService/onefuzzlib/ImageOperations.cs b/src/ApiService/ApiService/onefuzzlib/ImageOperations.cs index 81975a388b..31ec6a13fc 100644 --- a/src/ApiService/ApiService/onefuzzlib/ImageOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ImageOperations.cs @@ -65,7 +65,7 @@ ex is NullReferenceException } } else { try { - name = (await _context.Creds.GetResourceGroupResource().GetDiskImages().GetAsync( + name = (await _context.Creds.GetResourceGroupResource().GetImages().GetAsync( parsed.Data.Name )).Value.Data.StorageProfile.OSDisk.OSType.ToString().ToLowerInvariant(); } catch (Exception ex) when ( @@ -96,15 +96,13 @@ ex is NullReferenceException version = imageInfo.Version; } - var vmImage = await subscription.GetVirtualMachineImageAsync( - region, - imageInfo.Publisher, - imageInfo.Offer, - imageInfo.Sku - , version - ); - - name = vmImage.Value.OSDiskImageOperatingSystem!.Value.ToString().ToLower(); + name = (await subscription.GetVirtualMachineImageAsync( + region, + imageInfo.Publisher, + imageInfo.Offer, + imageInfo.Sku + , version + )).Value.OSDiskImageOperatingSystem.ToString().ToLower(); } catch (RequestFailedException ex) { return OneFuzzResult.Error( ErrorCode.INVALID_IMAGE, diff --git a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs index 740963dffa..fb4330e136 100644 --- a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs @@ -16,6 +16,8 @@ public interface IIpOperations { public Async.Task GetPublicIp(ResourceIdentifier resourceId); + public Async.Task GetPublicIp(string resourceId); + public Async.Task GetIp(string resourceGroup, string name); public Async.Task DeleteNic(string resourceGroup, string name); @@ -86,6 +88,9 @@ public async System.Threading.Tasks.Task DeleteIp(string resourceGroup, string n var ips = await _networkInterfaceQuery.ListInstancePrivateIps(scalesetId, instance.OkV); return ips.FirstOrDefault(); } + public async Task GetPublicIp(string resourceId) { + return await GetPublicIp(new ResourceIdentifier(resourceId)); + } public async Task GetPublicIp(ResourceIdentifier resourceId) { // TODO: Parts of this function seem redundant, but I'm mirroring @@ -117,12 +122,15 @@ public async System.Threading.Tasks.Task DeleteIp(string resourceGroup, string n public async Task CreatePublicNic(string resourceGroup, string name, string region, Nsg? nsg) { _logTracer.Info($"creating nic for {resourceGroup}:{name} in {region}"); - var network = await Network.Create(region, _context); + var network = await Network.Init(region, _context); var subnetId = await network.GetId(); if (subnetId is null) { - await network.Create(); - return OneFuzzResultVoid.Ok; + var r = await network.Create(); + if (!r.IsOk) { + _logTracer.Error($"failed to create network in region {region} due to {r.ErrorV}"); + } + return r; } if (nsg != null) { diff --git a/src/ApiService/ApiService/onefuzzlib/Network.cs b/src/ApiService/ApiService/onefuzzlib/Network.cs index 9efdd28db4..f170fc38b3 100644 --- a/src/ApiService/ApiService/onefuzzlib/Network.cs +++ b/src/ApiService/ApiService/onefuzzlib/Network.cs @@ -22,7 +22,7 @@ public Network(string region, string group, string name, IOnefuzzContext context _networkConfig = networkConfig; } - public static async Async.Task Create(string region, IOnefuzzContext context) { + public static async Async.Task Init(string region, IOnefuzzContext context) { var group = context.Creds.GetBaseResourceGroup(); var instanceConfig = await context.ConfigOperations.Fetch(); var networkConfig = instanceConfig.NetworkConfig; diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index 3f4a774e60..4754e509b6 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -217,7 +217,7 @@ private static IEnumerable GetErrors(Proxy proxy, VirtualMachineData vmD } foreach (var status in instanceView.Statuses) { - if (status.Level == ComputeStatusLevelType.Error) { + if (status.Level == StatusLevelTypes.Error) { yield return $"code:{status.Code} status:{status.DisplayStatus} message:{status.Message}"; } } diff --git a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs index 1a68363be3..2f2fe602ff 100644 --- a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs @@ -198,7 +198,7 @@ public async Async.Task Setup(Scaleset scaleset) { //# This was done as part of the generated per-task setup script. _logTracer.Info($"{SCALESET_LOG_PREFIX} setup. scalset_id: {scaleset.ScalesetId}"); - var network = await Network.Create(scaleset.Region, _context); + var network = await Network.Init(scaleset.Region, _context); var networkId = await network.GetId(); if (networkId is null) { _logTracer.Info($"{SCALESET_LOG_PREFIX} creating network. region: {scaleset.Region} scaleset_id:{scaleset.ScalesetId}"); diff --git a/src/ApiService/ApiService/onefuzzlib/Subnet.cs b/src/ApiService/ApiService/onefuzzlib/Subnet.cs index 5825fdc19e..c20497db74 100644 --- a/src/ApiService/ApiService/onefuzzlib/Subnet.cs +++ b/src/ApiService/ApiService/onefuzzlib/Subnet.cs @@ -68,12 +68,17 @@ await _creds.GetResourceGroupResource().GetVirtualNetworks().CreateOrUpdateAsync } public async Async.Task GetSubnet(string vnetName, string subnetName) { - var vnet = await this.GetVnet(vnetName); + try { + var vnet = await this.GetVnet(vnetName); - if (vnet != null) { - return await vnet.GetSubnetAsync(subnetName); + if (vnet != null) { + return await vnet.GetSubnetAsync(subnetName); + } + return null; + } catch (RequestFailedException ex) when (ex.Status == 404) { + return null; } - return null; + } public async Task GetSubnetId(string name, string subnetName) { diff --git a/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs b/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs index a017f0dcef..e0f86cccba 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmExtensionWrapper.cs @@ -1,6 +1,7 @@ using Azure.Core; using Azure.ResourceManager.Compute; + namespace Microsoft.OneFuzz.Service { public class VMExtensionWrapper { public AzureLocation? Location { get; init; } @@ -27,7 +28,7 @@ public class VMExtensionWrapper { var protectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()); return (Name!, new VirtualMachineExtensionData(Location.Value) { - ExtensionType = TypePropertiesType, + TypePropertiesType = TypePropertiesType, Publisher = Publisher, TypeHandlerVersion = TypeHandlerVersion, AutoUpgradeMinorVersion = AutoUpgradeMinorVersion, @@ -49,7 +50,8 @@ public VirtualMachineScaleSetExtensionData GetAsVirtualMachineScaleSetExtension( var protectedSettings = ProtectedSettings ?? new BinaryData(new Dictionary()); return new VirtualMachineScaleSetExtensionData() { - ExtensionType = TypePropertiesType, + Name = Name, + TypePropertiesType = TypePropertiesType, Publisher = Publisher, TypeHandlerVersion = TypeHandlerVersion, AutoUpgradeMinorVersion = AutoUpgradeMinorVersion, @@ -62,3 +64,4 @@ public VirtualMachineScaleSetExtensionData GetAsVirtualMachineScaleSetExtension( } } + diff --git a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs index 22a714de74..f9f8f994c3 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Azure; -using Azure.Core; using Azure.ResourceManager.Compute; using Azure.ResourceManager.Compute.Models; using Newtonsoft.Json; @@ -68,7 +67,7 @@ public async Async.Task HasComponents(string name) { public async Task GetVm(string name) { // _logTracer.Debug($"getting vm: {name}"); try { - var result = await _context.Creds.GetResourceGroupResource().GetVirtualMachineAsync(name, InstanceViewType.InstanceView); + var result = await _context.Creds.GetResourceGroupResource().GetVirtualMachineAsync(name, InstanceViewTypes.InstanceView); if (result == null) { return null; } @@ -204,8 +203,7 @@ public async Task Create(Vm vm) { return (await vm.Value.GetVirtualMachineExtensionAsync(extensionName)).Value.Data; } catch (RequestFailedException ex) { - _logTracer.Exception(ex, $"extension {extensionName} does not exist"); - _logTracer.Info($"extension {extensionName} does not exist {ex}"); + _logTracer.Info($"extension does not exist {ex}"); return null; } } @@ -257,20 +255,20 @@ async Task CreateVm( } var vmParams = new VirtualMachineData(location) { - OSProfile = new VirtualMachineOSProfile { + OSProfile = new OSProfile { ComputerName = "node", AdminUsername = "onefuzz", }, - HardwareProfile = new VirtualMachineHardwareProfile { + HardwareProfile = new HardwareProfile { VmSize = vmSku, }, - StorageProfile = new VirtualMachineStorageProfile { + StorageProfile = new StorageProfile { ImageReference = GenerateImageReference(image), }, - NetworkProfile = new VirtualMachineNetworkProfile(), + NetworkProfile = new NetworkProfile(), }; - vmParams.NetworkProfile.NetworkInterfaces.Add(new VirtualMachineNetworkInterfaceReference { Id = nic.Id }); + vmParams.NetworkProfile.NetworkInterfaces.Add(new NetworkInterfaceReference { Id = nic.Id }); var imageOs = await _context.ImageOperations.GetOs(location, image); if (!imageOs.IsOk) { @@ -287,7 +285,7 @@ async Task CreateVm( DisablePasswordAuthentication = true, }; vmParams.OSProfile.LinuxConfiguration.SshPublicKeys.Add( - new SshPublicKeyConfiguration { + new SshPublicKeyInfo { Path = "/home/onefuzz/.ssh/authorized_keys", KeyData = sshPublicKey } @@ -334,7 +332,7 @@ private static ImageReference GenerateImageReference(string image) { var imageRef = new ImageReference(); if (image.StartsWith("/", StringComparison.Ordinal)) { - imageRef.Id = new ResourceIdentifier(image); + imageRef.Id = image; } else { var imageVal = image.Split(":", 4); imageRef.Publisher = imageVal[0]; diff --git a/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs index 62d727112a..5d556feea4 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmssOperations.cs @@ -262,15 +262,15 @@ public async Async.Task CreateVmss( Sku = new ComputeSku() { Name = vmSku, Capacity = vmCount }, Overprovision = false, SinglePlacementGroup = false, - UpgradePolicy = new VirtualMachineScaleSetUpgradePolicy() { Mode = VirtualMachineScaleSetUpgradeMode.Manual }, + UpgradePolicy = new UpgradePolicy() { Mode = UpgradeMode.Manual }, Identity = new ManagedServiceIdentity(managedServiceIdentityType: ManagedServiceIdentityType.UserAssigned), }; vmssData.Identity.UserAssignedIdentities.Add(_creds.GetScalesetIdentityResourcePath(), new UserAssignedIdentity()); - vmssData.VirtualMachineProfile = new VirtualMachineScaleSetVmProfile() { Priority = VirtualMachinePriorityType.Regular }; + vmssData.VirtualMachineProfile = new VirtualMachineScaleSetVmProfile() { Priority = VirtualMachinePriorityTypes.Regular }; var imageRef = new ImageReference(); if (image.StartsWith('/')) { - imageRef.Id = new ResourceIdentifier(image); + imageRef.Id = image; } else { var info = IImageOperations.GetImageInfo(image); imageRef.Publisher = info.Publisher; @@ -303,7 +303,7 @@ public async Async.Task CreateVmss( case Os.Linux: vmssData.VirtualMachineProfile.OSProfile.LinuxConfiguration = new LinuxConfiguration(); vmssData.VirtualMachineProfile.OSProfile.LinuxConfiguration.DisablePasswordAuthentication = true; - var i = new SshPublicKeyConfiguration() { KeyData = sshPublicKey, Path = "/home/onefuzz/.ssh/authorized_keys" }; + var i = new SshPublicKeyInfo() { KeyData = sshPublicKey, Path = "/home/onefuzz/.ssh/authorized_keys" }; vmssData.VirtualMachineProfile.OSProfile.LinuxConfiguration.SshPublicKeys.Add(i); break; default: @@ -311,10 +311,10 @@ public async Async.Task CreateVmss( } if (ephemeralOsDisks) { - vmssData.VirtualMachineProfile.StorageProfile.OSDisk = new VirtualMachineScaleSetOSDisk(DiskCreateOptionType.FromImage); + vmssData.VirtualMachineProfile.StorageProfile.OSDisk = new VirtualMachineScaleSetOSDisk(DiskCreateOptionTypes.FromImage); vmssData.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings = new DiffDiskSettings(); - vmssData.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings.Option = DiffDiskOption.Local; - vmssData.VirtualMachineProfile.StorageProfile.OSDisk.Caching = CachingType.ReadOnly; + vmssData.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings.Option = DiffDiskOptions.Local; + vmssData.VirtualMachineProfile.StorageProfile.OSDisk.Caching = CachingTypes.ReadOnly; } if (spotInstance.HasValue && spotInstance.Value) { @@ -323,8 +323,8 @@ public async Async.Task CreateVmss( // // https://docs.microsoft.com/en-us/azure/ // virtual-machine-scale-sets/use-spot#resource-manager-templates - vmssData.VirtualMachineProfile.EvictionPolicy = VirtualMachineEvictionPolicyType.Deallocate; - vmssData.VirtualMachineProfile.Priority = VirtualMachinePriorityType.Spot; + vmssData.VirtualMachineProfile.EvictionPolicy = VirtualMachineEvictionPolicyTypes.Deallocate; + vmssData.VirtualMachineProfile.Priority = VirtualMachinePriorityTypes.Spot; vmssData.VirtualMachineProfile.BillingMaxPrice = 1.0; } @@ -370,14 +370,14 @@ public Async.Task> ListAvailableSkus(string region) entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)); var sub = _creds.GetSubscriptionResource(); - var skus = sub.GetComputeResourceSkusAsync(filter: TableClient.CreateQueryFilter($"location eq '{region}'")); + var skus = sub.GetResourceSkusAsync(filter: TableClient.CreateQueryFilter($"location eq '{region}'")); var skuNames = new List(); await foreach (var sku in skus) { var available = true; if (sku.Restrictions is not null) { foreach (var restriction in sku.Restrictions) { - if (restriction.RestrictionsType == ComputeResourceSkuRestrictionsType.Location && + if (restriction.RestrictionsType == ResourceSkuRestrictionsType.Location && restriction.Values.Contains(region, StringComparer.OrdinalIgnoreCase)) { available = false; break; diff --git a/src/ApiService/ApiService/packages.lock.json b/src/ApiService/ApiService/packages.lock.json index f85a701385..f990631d94 100644 --- a/src/ApiService/ApiService/packages.lock.json +++ b/src/ApiService/ApiService/packages.lock.json @@ -65,12 +65,12 @@ }, "Azure.ResourceManager.Compute": { "type": "Direct", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "dmDhokNFcyreIYZMkha8OsLmch0hW/sdOA2nxN4MPVd8KJgGWB8DxCZWwG7qhh59RInKtmWOP8BnBS5mN+W5wQ==", + "requested": "[1.0.0-beta.8, )", + "resolved": "1.0.0-beta.8", + "contentHash": "rYYjjmEdmcOa8O4UgO/bdJ/qQclNZjuHdalxRJ0AhUHCORcM1f1BbIKR9CoN83IpfuEE+X+n5XY9QZcKvfrGVA==", "dependencies": { - "Azure.Core": "1.25.0", - "Azure.ResourceManager": "1.2.0", + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", "System.Text.Json": "4.7.2" } }, diff --git a/src/ApiService/FunctionalTests/1f-api/Helpers.cs b/src/ApiService/FunctionalTests/1f-api/Helpers.cs index eae7ce6ede..a4a8916c22 100644 --- a/src/ApiService/FunctionalTests/1f-api/Helpers.cs +++ b/src/ApiService/FunctionalTests/1f-api/Helpers.cs @@ -8,13 +8,15 @@ namespace FunctionalTests { public class Helpers { - public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os, string? region = null, int numNodes = 2 ) { + public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os = "linux", string? region = null, int numNodes = 2 ) { + var image = (os == "linux") ? ScalesetApi.Image_Ubuntu_20_04 : ScalesetApi.ImageWindows; + var newPoolId = Guid.NewGuid().ToString(); var newPoolName = PoolApi.TestPoolPrefix + newPoolId; var newPool = await poolApi.Create(newPoolName, os); Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}"); - var newScalesetResult = await scalesetApi.Create(newPool.OkV!.Name, numNodes, region: region); + var newScalesetResult = await scalesetApi.Create(newPool.OkV!.Name, numNodes, region: region, image: image); Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}"); var newScaleset = newScalesetResult.OkV!; diff --git a/src/ApiService/FunctionalTests/1f-api/Node.cs b/src/ApiService/FunctionalTests/1f-api/Node.cs index bd4be3a569..a814f4a039 100644 --- a/src/ApiService/FunctionalTests/1f-api/Node.cs +++ b/src/ApiService/FunctionalTests/1f-api/Node.cs @@ -41,13 +41,13 @@ public NodeApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOut base(endpoint, "/api/Node", request, output) { } - public async Task Update(Guid machineId, bool? debugKeepNode = null) { + public async Task Update(Guid machineId, bool? debugKeepNode = null) { var j = new JsonObject(); if (debugKeepNode is not null) j.Add("debug_keep_node", JsonValue.Create(debugKeepNode)); j.Add("machine_id", JsonValue.Create(machineId)); - return await Post(j); + return Return(await Post(j)); } public async Task, Error>> Get(Guid? machineId = null, List? state = null, Guid? scalesetId = null, string? poolName = null) { var j = new JsonObject(); @@ -66,10 +66,10 @@ public async Task, Error>> Get(Guid? machineId = null, return IEnumerableResult(await Get(j)); } - public async Task Patch(Guid machineId) { + public async Task Patch(Guid machineId) { var j = new JsonObject(); j.Add("machine_id", JsonValue.Create(machineId)); - return await Patch(j); + return Return(await Patch(j)); } public async Task Delete(Guid machineId) { diff --git a/src/ApiService/FunctionalTests/1f-api/Pool.cs b/src/ApiService/FunctionalTests/1f-api/Pool.cs index 5abb7f9ae0..faa0bdf4e9 100644 --- a/src/ApiService/FunctionalTests/1f-api/Pool.cs +++ b/src/ApiService/FunctionalTests/1f-api/Pool.cs @@ -28,17 +28,21 @@ public PoolApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOut } public async Task Delete(string name, bool now = true) { + _output.WriteLine($"deleting pool: {name}, now: {now}"); var root = new JsonObject(); root.Add("name", JsonValue.Create(name)); root.Add("now", JsonValue.Create(now)); return Return(await Delete(root)); } - public async Task, Error>> Get(string? poolName = null, string? poolId = null, string? state = null) { + public async Task, Error>> Get(string? name = null, string? id = null, string? state = null) { var root = new JsonObject(); - root.Add("pool_id", poolId); - root.Add("name", poolName); - root.Add("state", state); + if (id is not null) + root.Add("pool_id", id); + if (name is not null) + root.Add("name", name); + if (state is not null) + root.Add("state", state); var res = await Get(root); return IEnumerableResult(res); @@ -60,6 +64,8 @@ public async Task DeleteAll() { } public async Task> Create(string poolName, string os, string arch = "x86_64") { + _output.WriteLine($"creating new pool {poolName} os: {os}"); + var rootPoolCreate = new JsonObject(); rootPoolCreate.Add("name", poolName); rootPoolCreate.Add("os", os); diff --git a/src/ApiService/FunctionalTests/1f-api/Proxy.cs b/src/ApiService/FunctionalTests/1f-api/Proxy.cs index 340c4a72dd..071fa7e698 100644 --- a/src/ApiService/FunctionalTests/1f-api/Proxy.cs +++ b/src/ApiService/FunctionalTests/1f-api/Proxy.cs @@ -94,9 +94,12 @@ public ProxyApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOu public async Task, Error>> Get(Guid? scalesetId = null, Guid? machineId = null, int? dstPort = null) { var root = new JsonObject(); - root.Add("scaleset_id", scalesetId); - root.Add("machine_id", machineId); - root.Add("dst_port", dstPort); + if (scalesetId is not null) + root.Add("scaleset_id", scalesetId); + if (machineId is not null) + root.Add("machine_id", machineId); + if (dstPort is not null) + root.Add("dst_port", dstPort); var r = await Get(root); if (Error.IsError(r)) { diff --git a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs index 4f72e683c5..d2b6b93085 100644 --- a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs +++ b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs @@ -68,6 +68,7 @@ public Scaleset() { } public class ScalesetApi : ApiBase { public const string Image_Ubuntu_20_04 = "Canonical:0001-com-ubuntu-server-focal:20_04-lts:latest"; + public const string ImageWindows = "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest"; public ScalesetApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) : base(endpoint, "/api/Scaleset", request, output) { } @@ -86,6 +87,7 @@ public async Task, Error>> Get(Guid? id = null, str } public async Task> Create(string poolName, int size, string? region=null, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { + _output.WriteLine($"Creating scaleset in pool {poolName}, size: {size}"); var rootScalesetCreate = new JsonObject(); rootScalesetCreate.Add("pool_name", poolName); rootScalesetCreate.Add("vm_sku", vmSku); diff --git a/src/ApiService/FunctionalTests/TestNode.cs b/src/ApiService/FunctionalTests/TestNode.cs index cbf61b523a..781996b54a 100644 --- a/src/ApiService/FunctionalTests/TestNode.cs +++ b/src/ApiService/FunctionalTests/TestNode.cs @@ -46,11 +46,24 @@ async Task GetPatchPostDelete() { scaleset = await _scalesetApi.WaitWhile(scaleset.ScalesetId, sc => sc.State == "init" || sc.State == "setup"); Assert.True(scaleset.Nodes!.Count > 0); + var nodeState = scaleset.Nodes!.First(); + var nodeResult = await _nodeApi.Get(nodeState.MachineId); + Assert.True(nodeResult.IsOk, $"failed to get node due to {nodeResult.ErrorV}"); + var node = nodeResult.OkV!.First(); + node = await _nodeApi.WaitWhile(node.MachineId, n => n.State == "init" || n.State == "setup"); + var r = await _nodeApi.Patch(node.MachineId); + Assert.True(r.Result); - } + var rr = await _nodeApi.Update(node.MachineId, false); + + var d = await _nodeApi.Delete(node.MachineId); + Assert.True(d.Result); + var deletePool = await _poolApi.Delete(pool.Name); + Assert.True(deletePool.Result); + } } } diff --git a/src/ApiService/FunctionalTests/TestPool.cs b/src/ApiService/FunctionalTests/TestPool.cs index 56fb7668d4..01562a3e1b 100644 --- a/src/ApiService/FunctionalTests/TestPool.cs +++ b/src/ApiService/FunctionalTests/TestPool.cs @@ -19,7 +19,7 @@ public TestPool(ITestOutputHelper output) { [Fact] async Task GetNonExistentPool() { - var p = await _poolApi.Get(poolName: Guid.NewGuid().ToString()); + var p = await _poolApi.Get(name: Guid.NewGuid().ToString()); Assert.True(p.ErrorV!.UnableToFindPoolError); } diff --git a/src/ApiService/FunctionalTests/TestScaleset.cs b/src/ApiService/FunctionalTests/TestScaleset.cs index 644cd04cfd..4af39e5e6c 100644 --- a/src/ApiService/FunctionalTests/TestScaleset.cs +++ b/src/ApiService/FunctionalTests/TestScaleset.cs @@ -34,9 +34,8 @@ public async Task GetScalesets() { } } - [Fact] - public async Task CreateAndDelete() { - var (newPool, newScaleset) = await Helpers.CreatePoolAndScaleset(_poolApi, _scalesetApi, "linux"); + public async Task CreateAndDelete(string os) { + var (newPool, newScaleset) = await Helpers.CreatePoolAndScaleset(_poolApi, _scalesetApi, os); var newScalesetResultAgain = await _scalesetApi.Create(newPool.Name, 2); var newScalesetResultAgainAgain = await _scalesetApi.Create(newPool.Name, 5); @@ -113,6 +112,18 @@ public async Task CreateAndDelete() { Assert.True(patch1.ErrorV!.UnableToFindScalesetError); } return; + + } + + + [Fact] + public async Task CreateAndDeleteLinux() { + await CreateAndDelete("linux"); + } + + [Fact] + public async Task CreateAndDeleteWindows() { + await CreateAndDelete("windows"); } } } diff --git a/src/ApiService/IntegrationTests/packages.lock.json b/src/ApiService/IntegrationTests/packages.lock.json index ba79ae0f35..4e78e7fd12 100644 --- a/src/ApiService/IntegrationTests/packages.lock.json +++ b/src/ApiService/IntegrationTests/packages.lock.json @@ -103,11 +103,11 @@ }, "Azure.ResourceManager.Compute": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "dmDhokNFcyreIYZMkha8OsLmch0hW/sdOA2nxN4MPVd8KJgGWB8DxCZWwG7qhh59RInKtmWOP8BnBS5mN+W5wQ==", + "resolved": "1.0.0-beta.8", + "contentHash": "rYYjjmEdmcOa8O4UgO/bdJ/qQclNZjuHdalxRJ0AhUHCORcM1f1BbIKR9CoN83IpfuEE+X+n5XY9QZcKvfrGVA==", "dependencies": { - "Azure.Core": "1.25.0", - "Azure.ResourceManager": "1.2.0", + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", "System.Text.Json": "4.7.2" } }, @@ -2153,7 +2153,7 @@ "Azure.Identity": "1.6.0", "Azure.Messaging.EventGrid": "4.10.0", "Azure.ResourceManager": "1.3.1", - "Azure.ResourceManager.Compute": "1.0.0", + "Azure.ResourceManager.Compute": "[1.0.0-beta.8, )", "Azure.ResourceManager.Monitor": "1.0.0-beta.2", "Azure.ResourceManager.Network": "1.0.0", "Azure.ResourceManager.Resources": "1.3.0", diff --git a/src/ApiService/Tests/packages.lock.json b/src/ApiService/Tests/packages.lock.json index 4676b47a25..2cc8b530c8 100644 --- a/src/ApiService/Tests/packages.lock.json +++ b/src/ApiService/Tests/packages.lock.json @@ -131,11 +131,11 @@ }, "Azure.ResourceManager.Compute": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "dmDhokNFcyreIYZMkha8OsLmch0hW/sdOA2nxN4MPVd8KJgGWB8DxCZWwG7qhh59RInKtmWOP8BnBS5mN+W5wQ==", + "resolved": "1.0.0-beta.8", + "contentHash": "rYYjjmEdmcOa8O4UgO/bdJ/qQclNZjuHdalxRJ0AhUHCORcM1f1BbIKR9CoN83IpfuEE+X+n5XY9QZcKvfrGVA==", "dependencies": { - "Azure.Core": "1.25.0", - "Azure.ResourceManager": "1.2.0", + "Azure.Core": "1.24.0", + "Azure.ResourceManager": "1.0.0", "System.Text.Json": "4.7.2" } }, @@ -2297,7 +2297,7 @@ "Azure.Identity": "1.6.0", "Azure.Messaging.EventGrid": "4.10.0", "Azure.ResourceManager": "1.3.1", - "Azure.ResourceManager.Compute": "1.0.0", + "Azure.ResourceManager.Compute": "[1.0.0-beta.8, )", "Azure.ResourceManager.Monitor": "1.0.0-beta.2", "Azure.ResourceManager.Network": "1.0.0", "Azure.ResourceManager.Resources": "1.3.0", From 9e346b13a0c0359530e13cbba368626eae179202 Mon Sep 17 00:00:00 2001 From: stas Date: Wed, 31 Aug 2022 12:37:03 -0700 Subject: [PATCH 3/5] format --- src/ApiService/ApiService/onefuzzlib/Subnet.cs | 2 +- src/ApiService/FunctionalTests/1f-api/Helpers.cs | 2 +- src/ApiService/FunctionalTests/1f-api/Proxy.cs | 2 +- src/ApiService/FunctionalTests/1f-api/Scaleset.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Subnet.cs b/src/ApiService/ApiService/onefuzzlib/Subnet.cs index c20497db74..affe69a56f 100644 --- a/src/ApiService/ApiService/onefuzzlib/Subnet.cs +++ b/src/ApiService/ApiService/onefuzzlib/Subnet.cs @@ -78,7 +78,7 @@ await _creds.GetResourceGroupResource().GetVirtualNetworks().CreateOrUpdateAsync } catch (RequestFailedException ex) when (ex.Status == 404) { return null; } - + } public async Task GetSubnetId(string name, string subnetName) { diff --git a/src/ApiService/FunctionalTests/1f-api/Helpers.cs b/src/ApiService/FunctionalTests/1f-api/Helpers.cs index a4a8916c22..42e21a5e71 100644 --- a/src/ApiService/FunctionalTests/1f-api/Helpers.cs +++ b/src/ApiService/FunctionalTests/1f-api/Helpers.cs @@ -8,7 +8,7 @@ namespace FunctionalTests { public class Helpers { - public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os = "linux", string? region = null, int numNodes = 2 ) { + public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os = "linux", string? region = null, int numNodes = 2) { var image = (os == "linux") ? ScalesetApi.Image_Ubuntu_20_04 : ScalesetApi.ImageWindows; var newPoolId = Guid.NewGuid().ToString(); diff --git a/src/ApiService/FunctionalTests/1f-api/Proxy.cs b/src/ApiService/FunctionalTests/1f-api/Proxy.cs index 071fa7e698..98f51a6762 100644 --- a/src/ApiService/FunctionalTests/1f-api/Proxy.cs +++ b/src/ApiService/FunctionalTests/1f-api/Proxy.cs @@ -50,7 +50,7 @@ public ProxyGetResult() { } public string? Ip { get { JsonElement ip; - if (_e.TryGetProperty("ip", out ip)){ + if (_e.TryGetProperty("ip", out ip)) { if (ip.ValueKind == JsonValueKind.Null) { return null; } else { diff --git a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs index d2b6b93085..969e913f41 100644 --- a/src/ApiService/FunctionalTests/1f-api/Scaleset.cs +++ b/src/ApiService/FunctionalTests/1f-api/Scaleset.cs @@ -86,7 +86,7 @@ public async Task, Error>> Get(Guid? id = null, str return IEnumerableResult(res); } - public async Task> Create(string poolName, int size, string? region=null, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { + public async Task> Create(string poolName, int size, string? region = null, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { _output.WriteLine($"Creating scaleset in pool {poolName}, size: {size}"); var rootScalesetCreate = new JsonObject(); rootScalesetCreate.Add("pool_name", poolName); From 106f0ccd2c1bf93d0bd90ad9715a0ca5e29a0e15 Mon Sep 17 00:00:00 2001 From: stas Date: Wed, 31 Aug 2022 15:41:47 -0700 Subject: [PATCH 4/5] found bug, i think... --- src/ApiService/ApiService/onefuzzlib/Creds.cs | 2 +- src/ApiService/ApiService/onefuzzlib/Extension.cs | 6 +++--- src/ApiService/FunctionalTests/1f-api/Pool.cs | 1 - src/ApiService/FunctionalTests/TestScaleset.cs | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index fe81bef3cb..ae0b0e4fd4 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -122,7 +122,7 @@ public Async.Task GetScalesetPrincipalId() { var resource = await uid.GetAsync(); var principalId = resource.Value.Data.Properties.ToObjectFromJson().principalId; - return new Guid(principalId); + return Guid.Parse(principalId); }); } diff --git a/src/ApiService/ApiService/onefuzzlib/Extension.cs b/src/ApiService/ApiService/onefuzzlib/Extension.cs index 22f8c447f7..ca3af617e8 100644 --- a/src/ApiService/ApiService/onefuzzlib/Extension.cs +++ b/src/ApiService/ApiService/onefuzzlib/Extension.cs @@ -264,6 +264,7 @@ public async Async.Task AgentConfig(AzureLocation region, Os await UpdateManagedScripts(); var urlsUpdated = urls ?? new(); + var managedIdentity = JsonSerializer.Serialize(new { ManagedIdentity = new Dictionary() }, _extensionSerializerOptions); if (vmOs == Os.Windows) { var vmScripts = await ConfigUrl(new Container("vm-scripts"), "managed.ps1", withSas) ?? throw new Exception("failed to get VmScripts config url"); var toolsAzCopy = await ConfigUrl(new Container("tools"), "win64/azcopy.exe", withSas) ?? throw new Exception("failed to get toolsAzCopy config url"); @@ -286,7 +287,7 @@ public async Async.Task AgentConfig(AzureLocation region, Os TypeHandlerVersion = "1.9", AutoUpgradeMinorVersion = true, Settings = new BinaryData(JsonSerializer.Serialize(new { commandToExecute = toExecuteCmd, fileUris = urlsUpdated }, _extensionSerializerOptions)), - ProtectedSettings = new BinaryData(JsonSerializer.Serialize(new { managedIdentity = new Dictionary() }, _extensionSerializerOptions)) + ProtectedSettings = new BinaryData(managedIdentity) }; return extension; } else if (vmOs == Os.Linux) { @@ -301,7 +302,6 @@ public async Async.Task AgentConfig(AzureLocation region, Os var toExecuteCmd = $"sh setup.sh {mode.ToString().ToLowerInvariant()}"; var extensionSettings = JsonSerializer.Serialize(new { CommandToExecute = toExecuteCmd, FileUris = urlsUpdated }, _extensionSerializerOptions); - var protectedExtensionSettings = JsonSerializer.Serialize(new { ManagedIdentity = new Dictionary() }, _extensionSerializerOptions); var extension = new VMExtensionWrapper { Name = "CustomScript", @@ -312,7 +312,7 @@ public async Async.Task AgentConfig(AzureLocation region, Os ForceUpdateTag = Guid.NewGuid().ToString(), AutoUpgradeMinorVersion = true, Settings = new BinaryData(extensionSettings), - ProtectedSettings = new BinaryData(protectedExtensionSettings) + ProtectedSettings = new BinaryData(managedIdentity) }; return extension; } diff --git a/src/ApiService/FunctionalTests/1f-api/Pool.cs b/src/ApiService/FunctionalTests/1f-api/Pool.cs index faa0bdf4e9..ce5a61e617 100644 --- a/src/ApiService/FunctionalTests/1f-api/Pool.cs +++ b/src/ApiService/FunctionalTests/1f-api/Pool.cs @@ -56,7 +56,6 @@ public async Task DeleteAll() { foreach (var pool in pools.OkV) { if (pool.Name.StartsWith(TestPoolPrefix)) { - _output.WriteLine($"Deleting {pool.Name}"); var deleted = await Delete(pool.Name); Assert.True(deleted.Result); } diff --git a/src/ApiService/FunctionalTests/TestScaleset.cs b/src/ApiService/FunctionalTests/TestScaleset.cs index 4af39e5e6c..fd700798f7 100644 --- a/src/ApiService/FunctionalTests/TestScaleset.cs +++ b/src/ApiService/FunctionalTests/TestScaleset.cs @@ -91,7 +91,7 @@ public async Task CreateAndDelete(string os) { Assert.True(preDeleteScalesets.IsOk, $"failed to get pre-deleted scalesets due to: {preDeleteScalesets.ErrorV}"); var preDelete = preDeleteScalesets.OkV!.Where(sc => sc.PoolName == newPool.Name); - Assert.True(preDelete.Count() == 1); + Assert.True(preDelete.Count() == 3); Result, Error> deletedPool; do { From 27d1116dac0730a9c3311b85709749222aa25c7a Mon Sep 17 00:00:00 2001 From: stas Date: Wed, 31 Aug 2022 16:52:55 -0700 Subject: [PATCH 5/5] now should be fixed --- .../ApiService/ServiceConfiguration.cs | 50 +++++++++++-------- .../ApiService/onefuzzlib/Extension.cs | 3 +- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/ApiService/ApiService/ServiceConfiguration.cs b/src/ApiService/ApiService/ServiceConfiguration.cs index 5c55ccb62b..3eef5dae69 100644 --- a/src/ApiService/ApiService/ServiceConfiguration.cs +++ b/src/ApiService/ApiService/ServiceConfiguration.cs @@ -64,47 +64,55 @@ public ServiceConfiguration() { #endif } + private static string? GetEnv(string name) { + var v = Environment.GetEnvironmentVariable(name); + if (String.IsNullOrEmpty(v)) + return null; + + return v; + } + //TODO: Add environment variable to control where to write logs to public LogDestination[] LogDestinations { get; set; } //TODO: Get this from Environment variable public ApplicationInsights.DataContracts.SeverityLevel LogSeverityLevel => ApplicationInsights.DataContracts.SeverityLevel.Verbose; - public string? ApplicationInsightsAppId => Environment.GetEnvironmentVariable("APPINSIGHTS_APPID"); - public string? ApplicationInsightsInstrumentationKey => Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY"); + public string? ApplicationInsightsAppId => GetEnv("APPINSIGHTS_APPID"); + public string? ApplicationInsightsInstrumentationKey => GetEnv("APPINSIGHTS_INSTRUMENTATIONKEY"); - public string? AzureSignalRConnectionString => Environment.GetEnvironmentVariable("AzureSignalRConnectionString"); - public string? AzureSignalRServiceTransportType => Environment.GetEnvironmentVariable("AzureSignalRServiceTransportType"); + public string? AzureSignalRConnectionString => GetEnv("AzureSignalRConnectionString"); + public string? AzureSignalRServiceTransportType => GetEnv("AzureSignalRServiceTransportType"); - public string? AzureWebJobDisableHomePage { get => Environment.GetEnvironmentVariable("AzureWebJobsDisableHomepage"); } - public string? AzureWebJobStorage { get => Environment.GetEnvironmentVariable("AzureWebJobsStorage"); } + public string? AzureWebJobDisableHomePage { get => GetEnv("AzureWebJobsDisableHomepage"); } + public string? AzureWebJobStorage { get => GetEnv("AzureWebJobsStorage"); } - public string? DiagnosticsAzureBlobContainerSasUrl { get => Environment.GetEnvironmentVariable("DIAGNOSTICS_AZUREBLOBCONTAINERSASURL"); } - public string? DiagnosticsAzureBlobRetentionDays { get => Environment.GetEnvironmentVariable("DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS"); } + public string? DiagnosticsAzureBlobContainerSasUrl { get => GetEnv("DIAGNOSTICS_AZUREBLOBCONTAINERSASURL"); } + public string? DiagnosticsAzureBlobRetentionDays { get => GetEnv("DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS"); } - public string? MultiTenantDomain { get => Environment.GetEnvironmentVariable("MULTI_TENANT_DOMAIN"); } + public string? MultiTenantDomain { get => GetEnv("MULTI_TENANT_DOMAIN"); } - public string? OneFuzzDataStorage { get => Environment.GetEnvironmentVariable("ONEFUZZ_DATA_STORAGE"); } - public string? OneFuzzFuncStorage { get => Environment.GetEnvironmentVariable("ONEFUZZ_FUNC_STORAGE"); } - public string? OneFuzzInstance { get => Environment.GetEnvironmentVariable("ONEFUZZ_INSTANCE"); } - public string? OneFuzzInstanceName { get => Environment.GetEnvironmentVariable("ONEFUZZ_INSTANCE_NAME"); } - public string? OneFuzzKeyvault { get => Environment.GetEnvironmentVariable("ONEFUZZ_KEYVAULT"); } - public string? OneFuzzMonitor { get => Environment.GetEnvironmentVariable("ONEFUZZ_MONITOR"); } - 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? OneFuzzDataStorage { get => GetEnv("ONEFUZZ_DATA_STORAGE"); } + public string? OneFuzzFuncStorage { get => GetEnv("ONEFUZZ_FUNC_STORAGE"); } + public string? OneFuzzInstance { get => GetEnv("ONEFUZZ_INSTANCE"); } + public string? OneFuzzInstanceName { get => GetEnv("ONEFUZZ_INSTANCE_NAME"); } + public string? OneFuzzKeyvault { get => GetEnv("ONEFUZZ_KEYVAULT"); } + public string? OneFuzzMonitor { get => GetEnv("ONEFUZZ_MONITOR"); } + public string? OneFuzzOwner { get => GetEnv("ONEFUZZ_OWNER"); } + public string? OneFuzzResourceGroup { get => GetEnv("ONEFUZZ_RESOURCE_GROUP"); } + public string? OneFuzzTelemetry { get => GetEnv("ONEFUZZ_TELEMETRY"); } public string OneFuzzVersion { get { // version can be overridden by config: - return Environment.GetEnvironmentVariable("ONEFUZZ_VERSION") + return GetEnv("ONEFUZZ_VERSION") ?? _oneFuzzVersion ?? throw new InvalidOperationException("Unable to read OneFuzz version from assembly"); } } - public string? OneFuzzAllowOutdatedAgent => Environment.GetEnvironmentVariable("ONEFUZZ_ALLOW_OUTDATED_AGENT"); + public string? OneFuzzAllowOutdatedAgent => GetEnv("ONEFUZZ_ALLOW_OUTDATED_AGENT"); - public string OneFuzzNodeDisposalStrategy { get => Environment.GetEnvironmentVariable("ONEFUZZ_NODE_DISPOSAL_STRATEGY") ?? "scale_in"; } + public string OneFuzzNodeDisposalStrategy { get => GetEnv("ONEFUZZ_NODE_DISPOSAL_STRATEGY") ?? "scale_in"; } public string OneFuzzStoragePrefix => ""; // in production we never prefix the tables } diff --git a/src/ApiService/ApiService/onefuzzlib/Extension.cs b/src/ApiService/ApiService/onefuzzlib/Extension.cs index ca3af617e8..66836c0263 100644 --- a/src/ApiService/ApiService/onefuzzlib/Extension.cs +++ b/src/ApiService/ApiService/onefuzzlib/Extension.cs @@ -226,7 +226,8 @@ public static VMExtensionWrapper GenevaExtension(AzureLocation region) { ); var fileName = $"{pool.Name}/config.json"; - await _context.Containers.SaveBlob(new Container("vm-scripts"), fileName, (JsonSerializer.Serialize(config, EntityConverter.GetJsonSerializerOptions())), StorageType.Config); + var configJson = JsonSerializer.Serialize(config, EntityConverter.GetJsonSerializerOptions()); + await _context.Containers.SaveBlob(new Container("vm-scripts"), fileName, configJson, StorageType.Config); return await ConfigUrl(new Container("vm-scripts"), fileName, false); }