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
This commit is contained in:
Cheick Keita
2022-07-07 09:25:51 -07:00
committed by GitHub
parent 0f0fd8ee9c
commit aa2e76e7e9
11 changed files with 118 additions and 68 deletions

View File

@ -3,6 +3,7 @@
<AzureFunctionsVersion>v4</AzureFunctionsVersion> <AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<WarningLevel>5</WarningLevel> <WarningLevel>5</WarningLevel>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.ResourceManager.Monitor" Version="1.0.0-beta.2" /> <PackageReference Include="Azure.ResourceManager.Monitor" Version="1.0.0-beta.2" />

View File

@ -195,7 +195,7 @@ public record EventScalesetFailed(
public record EventScalesetResizeScheduled( public record EventScalesetResizeScheduled(
Guid ScalesetId, Guid ScalesetId,
PoolName PoolName, PoolName PoolName,
int size long size
) : BaseEvent(); ) : BaseEvent();

View File

@ -310,12 +310,13 @@ public record InstanceConfig
( (
[PartitionKey, RowKey] string InstanceName, [PartitionKey, RowKey] string InstanceName,
Guid[]? Admins, Guid[]? Admins,
bool? AllowPoolManagement,
string[] AllowedAadTenants, string[] AllowedAadTenants,
NetworkConfig NetworkConfig, [DefaultValue(InitMethod.DefaultConstructor)] NetworkConfig NetworkConfig,
NetworkSecurityGroupConfig ProxyNsgConfig, [DefaultValue(InitMethod.DefaultConstructor)] NetworkSecurityGroupConfig ProxyNsgConfig,
AzureVmExtensionConfig? Extensions, AzureVmExtensionConfig? Extensions,
string ProxyVmSku, string? ProxyVmSku,
bool AllowPoolManagement = true,
IDictionary<Endpoint, ApiAccessRule>? ApiAccessRules = null, IDictionary<Endpoint, ApiAccessRule>? ApiAccessRules = null,
IDictionary<PrincipalId, GroupId[]>? GroupMembership = null, IDictionary<PrincipalId, GroupId[]>? GroupMembership = null,
IDictionary<string, string>? VmTags = null, IDictionary<string, string>? VmTags = null,
@ -325,13 +326,13 @@ public record InstanceConfig
public InstanceConfig(string instanceName) : this( public InstanceConfig(string instanceName) : this(
instanceName, instanceName,
null, null,
true,
Array.Empty<string>(), Array.Empty<string>(),
new NetworkConfig(), new NetworkConfig(),
new NetworkSecurityGroupConfig(), new NetworkSecurityGroupConfig(),
null, null,
"Standard_B2s") { } "Standard_B2s",
public InstanceConfig() : this(String.Empty) { } true
) { }
public static List<Guid>? CheckAdmins(List<Guid>? value) { public static List<Guid>? CheckAdmins(List<Guid>? value) {
if (value is not null && value.Count == 0) { 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 //# At the moment, this only checks allowed_aad_tenants, however adding
//# support for 3rd party JWT validation is anticipated in a future release. //# support for 3rd party JWT validation is anticipated in a future release.
public ResultVoid<List<string>> CheckInstanceConfig() { public ResultVoid<List<string>> CheckInstanceConfig() {
@ -373,12 +376,12 @@ public record Scaleset(
string VmSku, string VmSku,
string Image, string Image,
Region Region, Region Region,
int Size, long Size,
bool SpotInstance, bool? SpotInstances,
bool EphemeralOsDisks, bool EphemeralOsDisks,
bool NeedsConfigUpdate, bool NeedsConfigUpdate,
Error? Error, Error? Error,
List<ScalesetNodeState> Nodes, List<ScalesetNodeState>? Nodes,
Guid? ClientId, Guid? ClientId,
Guid? ClientObjectId, Guid? ClientObjectId,
Dictionary<string, string> Tags Dictionary<string, string> Tags

View File

@ -112,6 +112,7 @@ public class Program {
.AddScoped<IOnefuzzContext, OnefuzzContext>() .AddScoped<IOnefuzzContext, OnefuzzContext>()
.AddScoped<IEndpointAuthorization, EndpointAuthorization>() .AddScoped<IEndpointAuthorization, EndpointAuthorization>()
.AddScoped<INodeMessageOperations, NodeMessageOperations>() .AddScoped<INodeMessageOperations, NodeMessageOperations>()
.AddScoped<ISubnet, Subnet>()
.AddSingleton<ICreds, Creds>() .AddSingleton<ICreds, Creds>()
.AddSingleton<IServiceConfig, ServiceConfiguration>() .AddSingleton<IServiceConfig, ServiceConfiguration>()

View File

@ -26,7 +26,7 @@ public class TestHooks {
_logAnalytics = logAnalytics; _logAnalytics = logAnalytics;
} }
[Function("Info")] [Function("_Info")]
public async Task<HttpResponseData> Info([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/info")] HttpRequestData req) { public async Task<HttpResponseData> Info([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/info")] HttpRequestData req) {
_log.Info("Creating function info response"); _log.Info("Creating function info response");
var response = req.CreateResponse(); var response = req.CreateResponse();

View File

@ -2,8 +2,7 @@
namespace Microsoft.OneFuzz.Service; namespace Microsoft.OneFuzz.Service;
public class TimerProxy {
public partial class TimerProxy {
private readonly ILogTracer _logger; private readonly ILogTracer _logger;
private readonly IOnefuzzContext _context; private readonly IOnefuzzContext _context;

View File

@ -145,7 +145,7 @@ public class Containers : IContainers {
public async Async.Task SaveBlob(Container container, string name, string data, StorageType storageType) { 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}"); 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<Guid> GetInstanceId() => _getInstanceId.Value; public Async.Task<Guid> GetInstanceId() => _getInstanceId.Value;

View File

@ -2,55 +2,50 @@
namespace Microsoft.OneFuzz.Service; 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 { // This was generated randomly and should be preserved moving forwards
public class Network { static Guid NETWORK_GUID_NAMESPACE = Guid.Parse("372977ad-b533-416a-b1b4-f770898e0b11");
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 public Network(string region, string group, string name, IOnefuzzContext context) {
static Guid NETWORK_GUID_NAMESPACE = Guid.Parse("372977ad-b533-416a-b1b4-f770898e0b11"); _region = region;
_group = group;
public Network(string region, string group, string name, IOnefuzzContext context) { _name = name;
_region = region; _context = context;
_group = group;
_name = name;
_context = context;
}
public static async Async.Task<Network> 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<SubnetResource?> GetSubnet() {
return _context.Subnet.GetSubnet(_name, _name);
}
internal Async.Task<VirtualNetworkResource?> GetVnet() {
return _context.Subnet.GetVnet(_name);
}
} }
public static async Async.Task<Network> 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<SubnetResource?> GetSubnet() {
return _context.Subnet.GetSubnet(_name, _name);
}
internal Async.Task<VirtualNetworkResource?> GetVnet() {
return _context.Subnet.GetVnet(_name);
}
} }

View File

@ -20,6 +20,21 @@ public abstract record EntityBase {
public abstract record StatefulEntityBase<T>([property: JsonIgnore] T State) : EntityBase() where T : Enum; public abstract record StatefulEntityBase<T>([property: JsonIgnore] T State) : EntityBase() where T : Enum;
/// 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 /// Indicates that the enum cases should no be renamed
[AttributeUsage(AttributeTargets.Enum)] [AttributeUsage(AttributeTargets.Enum)]
public class SerializeValueAttribute : Attribute { } public class SerializeValueAttribute : Attribute { }
@ -56,7 +71,15 @@ public enum EntityPropertyKind {
RowKey, RowKey,
Column 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<string, EntityProperty> properties, Func<object?[], object> constructor); public record EntityInfo(Type type, ILookup<string, EntityProperty> properties, Func<object?[], object> constructor);
class OnefuzzNamingPolicy : JsonNamingPolicy { class OnefuzzNamingPolicy : JsonNamingPolicy {
@ -108,6 +131,8 @@ public class EntityConverter {
var isPartitionkey = parameterInfo.GetCustomAttribute(typeof(PartitionKeyAttribute)) != null; var isPartitionkey = parameterInfo.GetCustomAttribute(typeof(PartitionKeyAttribute)) != null;
var discriminatorAttribute = typeof(T).GetProperty(name)?.GetCustomAttribute<TypeDiscrimnatorAttribute>(); var discriminatorAttribute = typeof(T).GetProperty(name)?.GetCustomAttribute<TypeDiscrimnatorAttribute>();
var defaultValueAttribute = parameterInfo.GetCustomAttribute<DefaultValueAttribute>();
(TypeDiscrimnatorAttribute, ITypeProvider)? discriminator = null; (TypeDiscrimnatorAttribute, ITypeProvider)? discriminator = null;
if (discriminatorAttribute != null) { if (discriminatorAttribute != null) {
@ -117,16 +142,16 @@ public class EntityConverter {
if (isPartitionkey) { 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) { 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) { if (!isPartitionkey && !isRowkey) {
var columnName = typeof(T).GetProperty(name)?.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? CaseConverter.PascalToSnake(name); var columnName = typeof(T).GetProperty(name)?.GetCustomAttribute<JsonPropertyNameAttribute>()?.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 fieldName = ef.columnName;
var obj = entity[fieldName]; var obj = entity[fieldName];
if (obj == null) { 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(); var objType = obj.GetType();

View File

@ -164,7 +164,7 @@ namespace Tests {
public static Gen<InstanceConfig> InstanceConfig() { public static Gen<InstanceConfig> InstanceConfig() {
return Arb.Generate<Tuple< return Arb.Generate<Tuple<
Tuple<string, Guid[]?, bool?, string[], NetworkConfig, NetworkSecurityGroupConfig, AzureVmExtensionConfig?>, Tuple<string, Guid[]?, bool, string[], NetworkConfig, NetworkSecurityGroupConfig, AzureVmExtensionConfig?>,
Tuple<string, IDictionary<string, ApiAccessRule>?, IDictionary<Guid, Guid[]>?, IDictionary<string, string>?, IDictionary<string, string>?>>>().Select( Tuple<string, IDictionary<string, ApiAccessRule>?, IDictionary<Guid, Guid[]>?, IDictionary<string, string>?, IDictionary<string, string>?>>>().Select(
arg => arg =>
new InstanceConfig( new InstanceConfig(
@ -221,7 +221,7 @@ namespace Tests {
Region: arg.Item1.Item6, Region: arg.Item1.Item6,
Size: arg.Item2.Item1, Size: arg.Item2.Item1,
SpotInstance: arg.Item2.Item2, SpotInstances: arg.Item2.Item2,
EphemeralOsDisks: arg.Item2.Item3, EphemeralOsDisks: arg.Item2.Item3,
NeedsConfigUpdate: arg.Item2.Item4, NeedsConfigUpdate: arg.Item2.Item4,
Error: arg.Item2.Item5, Error: arg.Item2.Item5,

View File

@ -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<TestIinit>(tableEntity);
Assert.Equal("testName", actual.testClass.Name);
Assert.Equal("default_test", actual.test);
}
} }
} }