From aa2e76e7e9bc82cacb9f2e067cef0fd5c75a8367 Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Thu, 7 Jul 2022 09:25:51 -0700 Subject: [PATCH] Fix execution of TimerProxy (#2133) * Fix execution of timerProxy - fixed behavior of Container.SaveBlob (file was not being overwritten) - added support for default value in the entity converter - fix scaleset size data type - added initialization of Subnet - renamed TestHook Info to _Info to prevent clashing of name function mame - added target framework to ApiServiceProject to help local debugging * fix unit tests * fix unit tests * remove unused property * fix typo * removing partial class TimerProxy --- src/ApiService/ApiService/ApiService.csproj | 1 + .../ApiService/OneFuzzTypes/Events.cs | 2 +- .../ApiService/OneFuzzTypes/Model.cs | 23 ++--- src/ApiService/ApiService/Program.cs | 1 + src/ApiService/ApiService/TestHooks.cs | 2 +- src/ApiService/ApiService/TimerProxy.cs | 3 +- .../ApiService/onefuzzlib/Containers.cs | 2 +- .../ApiService/onefuzzlib/Network.cs | 87 +++++++++---------- .../onefuzzlib/orm/EntityConverter.cs | 43 +++++++-- src/ApiService/Tests/OrmModelsTest.cs | 4 +- src/ApiService/Tests/OrmTest.cs | 18 ++++ 11 files changed, 118 insertions(+), 68 deletions(-) diff --git a/src/ApiService/ApiService/ApiService.csproj b/src/ApiService/ApiService/ApiService.csproj index e9bdc8434..33943977b 100644 --- a/src/ApiService/ApiService/ApiService.csproj +++ b/src/ApiService/ApiService/ApiService.csproj @@ -3,6 +3,7 @@ v4 Exe 5 + net6.0 diff --git a/src/ApiService/ApiService/OneFuzzTypes/Events.cs b/src/ApiService/ApiService/OneFuzzTypes/Events.cs index 0cc1f6fcb..549fab4d9 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Events.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Events.cs @@ -195,7 +195,7 @@ public record EventScalesetFailed( public record EventScalesetResizeScheduled( Guid ScalesetId, PoolName PoolName, - int size + long size ) : BaseEvent(); diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index e0c28cfbb..01d055db3 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -310,12 +310,13 @@ public record InstanceConfig ( [PartitionKey, RowKey] string InstanceName, Guid[]? Admins, - bool? AllowPoolManagement, + string[] AllowedAadTenants, - NetworkConfig NetworkConfig, - NetworkSecurityGroupConfig ProxyNsgConfig, + [DefaultValue(InitMethod.DefaultConstructor)] NetworkConfig NetworkConfig, + [DefaultValue(InitMethod.DefaultConstructor)] NetworkSecurityGroupConfig ProxyNsgConfig, AzureVmExtensionConfig? Extensions, - string ProxyVmSku, + string? ProxyVmSku, + bool AllowPoolManagement = true, IDictionary? ApiAccessRules = null, IDictionary? GroupMembership = null, IDictionary? VmTags = null, @@ -325,13 +326,13 @@ public record InstanceConfig public InstanceConfig(string instanceName) : this( instanceName, null, - true, Array.Empty(), new NetworkConfig(), new NetworkSecurityGroupConfig(), null, - "Standard_B2s") { } - public InstanceConfig() : this(String.Empty) { } + "Standard_B2s", + true + ) { } public static List? CheckAdmins(List? value) { if (value is not null && value.Count == 0) { @@ -341,6 +342,8 @@ public record InstanceConfig } } + public InstanceConfig() : this(String.Empty) { } + //# At the moment, this only checks allowed_aad_tenants, however adding //# support for 3rd party JWT validation is anticipated in a future release. public ResultVoid> CheckInstanceConfig() { @@ -373,12 +376,12 @@ public record Scaleset( string VmSku, string Image, Region Region, - int Size, - bool SpotInstance, + long Size, + bool? SpotInstances, bool EphemeralOsDisks, bool NeedsConfigUpdate, Error? Error, - List Nodes, + List? Nodes, Guid? ClientId, Guid? ClientObjectId, Dictionary Tags diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 9ad266456..4d2c527fd 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -112,6 +112,7 @@ public class Program { .AddScoped() .AddScoped() .AddScoped() + .AddScoped() .AddSingleton() .AddSingleton() diff --git a/src/ApiService/ApiService/TestHooks.cs b/src/ApiService/ApiService/TestHooks.cs index 84e1642c7..4e5c1079c 100644 --- a/src/ApiService/ApiService/TestHooks.cs +++ b/src/ApiService/ApiService/TestHooks.cs @@ -26,7 +26,7 @@ public class TestHooks { _logAnalytics = logAnalytics; } - [Function("Info")] + [Function("_Info")] public async Task Info([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/info")] HttpRequestData req) { _log.Info("Creating function info response"); var response = req.CreateResponse(); diff --git a/src/ApiService/ApiService/TimerProxy.cs b/src/ApiService/ApiService/TimerProxy.cs index 1120baf5a..82d49e29f 100644 --- a/src/ApiService/ApiService/TimerProxy.cs +++ b/src/ApiService/ApiService/TimerProxy.cs @@ -2,8 +2,7 @@ namespace Microsoft.OneFuzz.Service; - -public partial class TimerProxy { +public class TimerProxy { private readonly ILogTracer _logger; private readonly IOnefuzzContext _context; diff --git a/src/ApiService/ApiService/onefuzzlib/Containers.cs b/src/ApiService/ApiService/onefuzzlib/Containers.cs index 025714633..3a6d10868 100644 --- a/src/ApiService/ApiService/onefuzzlib/Containers.cs +++ b/src/ApiService/ApiService/onefuzzlib/Containers.cs @@ -145,7 +145,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.ContainerName} - {storageType}"); - await client.UploadBlobAsync(name, new BinaryData(data)); + await client.GetBlobClient(name).UploadAsync(new BinaryData(data), overwrite: true); } public Async.Task GetInstanceId() => _getInstanceId.Value; diff --git a/src/ApiService/ApiService/onefuzzlib/Network.cs b/src/ApiService/ApiService/onefuzzlib/Network.cs index 107da9197..a4f009650 100644 --- a/src/ApiService/ApiService/onefuzzlib/Network.cs +++ b/src/ApiService/ApiService/onefuzzlib/Network.cs @@ -2,55 +2,50 @@ namespace Microsoft.OneFuzz.Service; +public class Network { + private readonly string _name; + private readonly string _group; + private readonly string _region; + private readonly IOnefuzzContext _context; -public partial class TimerProxy { - public class Network { - private readonly string _name; - private readonly string _group; - private readonly string _region; - private readonly IOnefuzzContext _context; + // This was generated randomly and should be preserved moving forwards + static Guid NETWORK_GUID_NAMESPACE = Guid.Parse("372977ad-b533-416a-b1b4-f770898e0b11"); - // This was generated randomly and should be preserved moving forwards - static Guid NETWORK_GUID_NAMESPACE = Guid.Parse("372977ad-b533-416a-b1b4-f770898e0b11"); - - public Network(string region, string group, string name, IOnefuzzContext context) { - _region = region; - _group = group; - _name = name; - _context = context; - } - - public static async Async.Task Create(string region, IOnefuzzContext context) { - var group = context.Creds.GetBaseResourceGroup(); - var instanceConfig = await context.ConfigOperations.Fetch(); - var networkConfig = instanceConfig.NetworkConfig; - - // Network names will be calculated from the address_space/subnet - // *except* if they are the original values. This allows backwards - // compatibility to existing configs if you don't change the network - // configs. - - string name; - - if (networkConfig.AddressSpace == NetworkConfig.Default.AddressSpace && networkConfig.Subnet == NetworkConfig.Default.Subnet) { - name = region; - } else { - var networkId = Faithlife.Utility.GuidUtility.Create(NETWORK_GUID_NAMESPACE, string.Join("|", networkConfig.AddressSpace, networkConfig.Subnet), 5); - name = $"{region}-{networkId}"; - } - - - return new Network(region, group, name, context); - } - - public Async.Task GetSubnet() { - return _context.Subnet.GetSubnet(_name, _name); - } - - internal Async.Task GetVnet() { - return _context.Subnet.GetVnet(_name); - } + public Network(string region, string group, string name, IOnefuzzContext context) { + _region = region; + _group = group; + _name = name; + _context = context; } + public static async Async.Task Create(string region, IOnefuzzContext context) { + var group = context.Creds.GetBaseResourceGroup(); + var instanceConfig = await context.ConfigOperations.Fetch(); + var networkConfig = instanceConfig.NetworkConfig; + // Network names will be calculated from the address_space/subnet + // *except* if they are the original values. This allows backwards + // compatibility to existing configs if you don't change the network + // configs. + + string name; + + if (networkConfig.AddressSpace == NetworkConfig.Default.AddressSpace && networkConfig.Subnet == NetworkConfig.Default.Subnet) { + name = region; + } else { + var networkId = Faithlife.Utility.GuidUtility.Create(NETWORK_GUID_NAMESPACE, string.Join("|", networkConfig.AddressSpace, networkConfig.Subnet), 5); + name = $"{region}-{networkId}"; + } + + + return new Network(region, group, name, context); + } + + public Async.Task GetSubnet() { + return _context.Subnet.GetSubnet(_name, _name); + } + + internal Async.Task GetVnet() { + return _context.Subnet.GetVnet(_name); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs index 617680d32..afed38e96 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs @@ -20,6 +20,21 @@ public abstract record EntityBase { public abstract record StatefulEntityBase([property: JsonIgnore] T State) : EntityBase() where T : Enum; + +/// How the value is populated +public enum InitMethod { + //T() will be used + DefaultConstructor, +} +[AttributeUsage(AttributeTargets.Parameter)] +public class DefaultValueAttribute : Attribute { + + public InitMethod InitMethod { get; } + public DefaultValueAttribute(InitMethod initMethod) { + InitMethod = initMethod; + } +} + /// Indicates that the enum cases should no be renamed [AttributeUsage(AttributeTargets.Enum)] public class SerializeValueAttribute : Attribute { } @@ -56,7 +71,15 @@ public enum EntityPropertyKind { RowKey, Column } -public record EntityProperty(string name, string columnName, Type type, EntityPropertyKind kind, (TypeDiscrimnatorAttribute, ITypeProvider)? discriminator); +public record EntityProperty( + string name, + string columnName, + Type type, + EntityPropertyKind kind, + (TypeDiscrimnatorAttribute, ITypeProvider)? discriminator, + DefaultValueAttribute? defaultValue, + ParameterInfo parameterInfo + ); public record EntityInfo(Type type, ILookup properties, Func constructor); class OnefuzzNamingPolicy : JsonNamingPolicy { @@ -108,6 +131,8 @@ public class EntityConverter { var isPartitionkey = parameterInfo.GetCustomAttribute(typeof(PartitionKeyAttribute)) != null; var discriminatorAttribute = typeof(T).GetProperty(name)?.GetCustomAttribute(); + var defaultValueAttribute = parameterInfo.GetCustomAttribute(); + (TypeDiscrimnatorAttribute, ITypeProvider)? discriminator = null; if (discriminatorAttribute != null) { @@ -117,16 +142,16 @@ public class EntityConverter { if (isPartitionkey) { - yield return new EntityProperty(name, "PartitionKey", parameterType, EntityPropertyKind.PartitionKey, discriminator); + yield return new EntityProperty(name, "PartitionKey", parameterType, EntityPropertyKind.PartitionKey, discriminator, defaultValueAttribute, parameterInfo); } if (isRowkey) { - yield return new EntityProperty(name, "RowKey", parameterType, EntityPropertyKind.RowKey, discriminator); + yield return new EntityProperty(name, "RowKey", parameterType, EntityPropertyKind.RowKey, discriminator, defaultValueAttribute, parameterInfo); } if (!isPartitionkey && !isRowkey) { var columnName = typeof(T).GetProperty(name)?.GetCustomAttribute()?.Name ?? CaseConverter.PascalToSnake(name); - yield return new EntityProperty(name, columnName, parameterType, EntityPropertyKind.Column, discriminator); + yield return new EntityProperty(name, columnName, parameterType, EntityPropertyKind.Column, discriminator, defaultValueAttribute, parameterInfo); } } @@ -218,7 +243,15 @@ public class EntityConverter { var fieldName = ef.columnName; var obj = entity[fieldName]; if (obj == null) { - return null; + + if (ef.parameterInfo.HasDefaultValue) { + return ef.parameterInfo.DefaultValue; + } + + return ef.defaultValue switch { + DefaultValueAttribute { InitMethod: InitMethod.DefaultConstructor } => Activator.CreateInstance(ef.type), + _ => null, + }; } var objType = obj.GetType(); diff --git a/src/ApiService/Tests/OrmModelsTest.cs b/src/ApiService/Tests/OrmModelsTest.cs index f185d6f1b..642079217 100644 --- a/src/ApiService/Tests/OrmModelsTest.cs +++ b/src/ApiService/Tests/OrmModelsTest.cs @@ -164,7 +164,7 @@ namespace Tests { public static Gen InstanceConfig() { return Arb.Generate, + Tuple, Tuple?, IDictionary?, IDictionary?, IDictionary?>>>().Select( arg => new InstanceConfig( @@ -221,7 +221,7 @@ namespace Tests { Region: arg.Item1.Item6, Size: arg.Item2.Item1, - SpotInstance: arg.Item2.Item2, + SpotInstances: arg.Item2.Item2, EphemeralOsDisks: arg.Item2.Item3, NeedsConfigUpdate: arg.Item2.Item4, Error: arg.Item2.Item5, diff --git a/src/ApiService/Tests/OrmTest.cs b/src/ApiService/Tests/OrmTest.cs index 57b2868b2..ca871138a 100644 --- a/src/ApiService/Tests/OrmTest.cs +++ b/src/ApiService/Tests/OrmTest.cs @@ -380,6 +380,24 @@ namespace Tests { } + class TestClass { + public string Name { get; } + public TestClass() { + Name = "testName"; + } + } + record TestIinit([DefaultValue(InitMethod.DefaultConstructor)] TestClass testClass, string test = "default_test") : EntityBase(); + + [Fact] + public void TestInitValue() { + var entityConverter = new EntityConverter(); + var tableEntity = new TableEntity(); + var actual = entityConverter.ToRecord(tableEntity); + + Assert.Equal("testName", actual.testClass.Name); + Assert.Equal("default_test", actual.test); + } + } }