Skip to content

Commit

Permalink
Merge pull request #1623 from jdom/config-extensions
Browse files Browse the repository at this point in the history
Extension methods for using programmatic config
  • Loading branch information
sergeybykov committed Mar 30, 2016
2 parents 691208e + ac67594 commit 1077f5e
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 35 deletions.
15 changes: 9 additions & 6 deletions src/Orleans/Serialization/SerializationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ namespace Orleans.Serialization
/// </summary>
public static class SerializationManager
{
internal const string UseFullAssemblyNamesProperty = "UseFullAssemblyNames";
internal const string IndentJsonProperty = "IndentJSON";

/// <summary>
/// Deep copier function.
/// </summary>
Expand Down Expand Up @@ -2320,19 +2323,19 @@ public static JsonSerializerSettings GetDefaultJsonSerializerSettings()
/// <returns><see cref="JsonSerializerSettings" /></returns>
public static JsonSerializerSettings UpdateSerializerSettings(JsonSerializerSettings settings, IProviderConfiguration config)
{
if (config.Properties.ContainsKey("UseFullAssemblyNames"))
if (config.Properties.ContainsKey(UseFullAssemblyNamesProperty))
{
bool useFullAssemblyNames = false;
if (bool.TryParse(config.Properties["UseFullAssemblyNames"], out useFullAssemblyNames) && useFullAssemblyNames)
bool useFullAssemblyNames;
if (bool.TryParse(config.Properties[UseFullAssemblyNamesProperty], out useFullAssemblyNames) && useFullAssemblyNames)
{
settings.TypeNameAssemblyFormat = FormatterAssemblyStyle.Full;
}
}

if (config.Properties.ContainsKey("IndentJSON"))
if (config.Properties.ContainsKey(IndentJsonProperty))
{
bool indentJSON = false;
if (bool.TryParse(config.Properties["IndentJSON"], out indentJSON) && indentJSON)
bool indentJSON;
if (bool.TryParse(config.Properties[IndentJsonProperty], out indentJSON) && indentJSON)
{
settings.Formatting = Formatting.Indented;
}
Expand Down
1 change: 1 addition & 0 deletions src/OrleansAzureUtils/OrleansAzureUtils.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
<Compile Include="..\Build\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Providers\AzureConfigurationExtensions.cs" />
<Compile Include="Providers\Storage\AzureBlobStorage.cs" />
<Compile Include="Providers\Streams\PersistentStreams\AzureTableStorageStreamFailureHandler.cs" />
<Compile Include="Providers\Streams\PersistentStreams\StreamDeliveryFailureEntity.cs" />
Expand Down
83 changes: 83 additions & 0 deletions src/OrleansAzureUtils/Providers/AzureConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using Orleans.Serialization;
using Orleans.Storage;

namespace Orleans.Runtime.Configuration
{
/// <summary>
/// Extension methods for configuration classes specific to OrleansAzureUtils.dll
/// </summary>
public static class AzureConfigurationExtensions
{
/// <summary>
/// Adds a storage provider of type <see cref="Orleans.Storage.AzureTableStorage"/>.
/// </summary>
/// <param name="config">The cluster configuration object to add provider to.</param>
/// <param name="providerName">The provider name</param>
/// <param name="connectionString">The azure storage connection string.</param>
/// <param name="tableName">The table name where to store the state.</param>
/// <param name="deleteOnClear">Whether the provider deletes the state when <see cref="IStorageProvider.ClearStateAsync"/> is called.</param>
/// <param name="useJsonFormat">Whether is stores the content as JSON or as binary in Azure Table.</param>
/// <param name="useFullAssemblyNames">Whether to use full assembly names in the serialized JSON. This value is ignored if <paramref name="useJsonFormat"/> is false.</param>
/// <param name="indentJson">Whether to indent (pretty print) the JSON. This value is ignored if <paramref name="useJsonFormat"/> is false.</param>
public static void AddAzureTableStorageProvider(
this ClusterConfiguration config,
string providerName = "AzureTableStore",
string connectionString = null,
string tableName = AzureTableStorage.TableNamePropertyDefaultValue,
bool deleteOnClear = false,
bool useJsonFormat = false,
bool useFullAssemblyNames = false,
bool indentJson = false)
{
if (connectionString == null) throw new ArgumentNullException(nameof(connectionString));

var properties = new Dictionary<string, string>
{
{ AzureTableStorage.DataConnectionStringPropertyName, connectionString },
{ AzureTableStorage.TableNamePropertyName, tableName },
{ AzureTableStorage.DeleteOnClearPropertyName, deleteOnClear.ToString() },
{ AzureTableStorage.UseJsonFormatPropertyName, useJsonFormat.ToString() },
};

if (useJsonFormat)
{
properties.Add(SerializationManager.UseFullAssemblyNamesProperty, useFullAssemblyNames.ToString());
properties.Add(SerializationManager.IndentJsonProperty, indentJson.ToString());
}

config.Globals.RegisterStorageProvider<AzureTableStorage>(providerName, properties);
}

/// <summary>
/// Adds a storage provider of type <see cref="Orleans.Storage.AzureBlobStorage"/>.
/// </summary>
/// <param name="config">The cluster configuration object to add provider to.</param>
/// <param name="providerName">The provider name</param>
/// <param name="connectionString">The azure storage connection string.</param>
/// <param name="containerName">The container name where to store the state.</param>
/// <param name="useFullAssemblyNames">Whether to use full assembly names in the serialized JSON.</param>
/// <param name="indentJson">Whether to indent (pretty print) the JSON.</param>
public static void AddAzureBlobStorageProvider(
this ClusterConfiguration config,
string providerName = "AzureBlobStore",
string connectionString = null,
string containerName = AzureBlobStorage.ContainerNamePropertyDefaultValue,
bool useFullAssemblyNames = false,
bool indentJson = false)
{
if (connectionString == null) throw new ArgumentNullException(nameof(connectionString));

var properties = new Dictionary<string, string>
{
{ AzureBlobStorage.DataConnectionStringPropertyName, connectionString },
{ AzureBlobStorage.ContainerNamePropertyName, containerName },
{ SerializationManager.UseFullAssemblyNamesProperty, useFullAssemblyNames.ToString() },
{ SerializationManager.IndentJsonProperty, indentJson.ToString() },
};

config.Globals.RegisterStorageProvider<AzureBlobStorage>(providerName, properties);
}
}
}
12 changes: 8 additions & 4 deletions src/OrleansAzureUtils/Providers/Storage/AzureBlobStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ namespace Orleans.Storage
/// </example>
public class AzureBlobStorage : IStorageProvider
{
internal const string DataConnectionStringPropertyName = AzureTableStorage.DataConnectionStringPropertyName;
internal const string ContainerNamePropertyName = "ContainerName";
internal const string ContainerNamePropertyDefaultValue = "grainstate";

private JsonSerializerSettings jsonSettings;

private CloudBlobContainer container;
Expand All @@ -67,11 +71,11 @@ public async Task Init(string name, IProviderRuntime providerRuntime, IProviderC
this.Name = name;
this.jsonSettings = SerializationManager.UpdateSerializerSettings(SerializationManager.GetDefaultJsonSerializerSettings(), config);

if (!config.Properties.ContainsKey("DataConnectionString")) throw new BadProviderConfigException("The DataConnectionString setting has not been configured in the cloud role. Please add a DataConnectionString setting with a valid Azure Storage connection string.");
if (!config.Properties.ContainsKey(DataConnectionStringPropertyName)) throw new BadProviderConfigException($"The {DataConnectionStringPropertyName} setting has not been configured in the cloud role. Please add a {DataConnectionStringPropertyName} setting with a valid Azure Storage connection string.");

var account = CloudStorageAccount.Parse(config.Properties["DataConnectionString"]);
var account = CloudStorageAccount.Parse(config.Properties[DataConnectionStringPropertyName]);
var blobClient = account.CreateCloudBlobClient();
var containerName = config.Properties.ContainsKey("ContainerName") ? config.Properties["ContainerName"] : "grainstate";
var containerName = config.Properties.ContainsKey(ContainerNamePropertyName) ? config.Properties[ContainerNamePropertyName] : ContainerNamePropertyDefaultValue;
container = blobClient.GetContainerReference(containerName);
await container.CreateIfNotExistsAsync().ConfigureAwait(false);

Expand All @@ -87,7 +91,7 @@ public async Task Init(string name, IProviderRuntime providerRuntime, IProviderC

IEnumerable<string> FormatPropertyMessage(IProviderConfiguration config)
{
foreach (var property in new string[] { "ContainerName", "SerializeTypeNames", "PreserveReferencesHandling", "UseFullAssemblyNames", "IndentJSON" })
foreach (var property in new string[] { ContainerNamePropertyName, "SerializeTypeNames", "PreserveReferencesHandling", "UseFullAssemblyNames", "IndentJSON" })
{
if (!config.Properties.ContainsKey(property)) continue;
yield return string.Format("{0}={1}", property, config.Properties[property]);
Expand Down
26 changes: 13 additions & 13 deletions src/OrleansAzureUtils/Providers/Storage/AzureTableStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ namespace Orleans.Storage
/// </example>
public class AzureTableStorage : IStorageProvider, IRestExceptionDecoder
{
private const string DATA_CONNECTION_STRING = "DataConnectionString";
private const string TABLE_NAME_PROPERTY = "TableName";
private const string DELETE_ON_CLEAR_PROPERTY = "DeleteStateOnClear";
private const string GRAIN_STATE_TABLE_NAME_DEFAULT = "OrleansGrainState";
internal const string DataConnectionStringPropertyName = "DataConnectionString";
internal const string TableNamePropertyName = "TableName";
internal const string DeleteOnClearPropertyName = "DeleteStateOnClear";
internal const string UseJsonFormatPropertyName = "UseJsonFormat";
internal const string TableNamePropertyDefaultValue = "OrleansGrainState";
private string dataConnectionString;
private string tableName;
private string serviceId;
Expand All @@ -64,7 +65,6 @@ public class AzureTableStorage : IStorageProvider, IRestExceptionDecoder
private const string BINARY_DATA_PROPERTY_NAME = "Data";
private const string STRING_DATA_PROPERTY_NAME = "StringData";

private const string USE_JSON_FORMAT_PROPERTY = "UseJsonFormat";

private bool useJsonFormat;
private Newtonsoft.Json.JsonSerializerSettings jsonSettings;
Expand All @@ -80,7 +80,7 @@ public class AzureTableStorage : IStorageProvider, IRestExceptionDecoder
/// <summary> Default constructor </summary>
public AzureTableStorage()
{
tableName = GRAIN_STATE_TABLE_NAME_DEFAULT;
tableName = TableNamePropertyDefaultValue;
id = Interlocked.Increment(ref counter);
}

Expand All @@ -91,24 +91,24 @@ public Task Init(string name, IProviderRuntime providerRuntime, IProviderConfigu
Name = name;
serviceId = providerRuntime.ServiceId.ToString();

if (!config.Properties.ContainsKey(DATA_CONNECTION_STRING) || string.IsNullOrWhiteSpace(config.Properties[DATA_CONNECTION_STRING]))
if (!config.Properties.ContainsKey(DataConnectionStringPropertyName) || string.IsNullOrWhiteSpace(config.Properties[DataConnectionStringPropertyName]))
throw new ArgumentException("DataConnectionString property not set");

dataConnectionString = config.Properties["DataConnectionString"];

if (config.Properties.ContainsKey(TABLE_NAME_PROPERTY))
tableName = config.Properties[TABLE_NAME_PROPERTY];
if (config.Properties.ContainsKey(TableNamePropertyName))
tableName = config.Properties[TableNamePropertyName];

isDeleteStateOnClear = config.Properties.ContainsKey(DELETE_ON_CLEAR_PROPERTY) &&
"true".Equals(config.Properties[DELETE_ON_CLEAR_PROPERTY], StringComparison.OrdinalIgnoreCase);
isDeleteStateOnClear = config.Properties.ContainsKey(DeleteOnClearPropertyName) &&
"true".Equals(config.Properties[DeleteOnClearPropertyName], StringComparison.OrdinalIgnoreCase);

Log = providerRuntime.GetLogger("Storage.AzureTableStorage." + id);

var initMsg = string.Format("Init: Name={0} ServiceId={1} Table={2} DeleteStateOnClear={3}",
Name, serviceId, tableName, isDeleteStateOnClear);

if (config.Properties.ContainsKey(USE_JSON_FORMAT_PROPERTY))
useJsonFormat = "true".Equals(config.Properties[USE_JSON_FORMAT_PROPERTY], StringComparison.OrdinalIgnoreCase);
if (config.Properties.ContainsKey(UseJsonFormatPropertyName))
useJsonFormat = "true".Equals(config.Properties[UseJsonFormatPropertyName], StringComparison.OrdinalIgnoreCase);

this.jsonSettings = SerializationManager.UpdateSerializerSettings(SerializationManager.GetDefaultJsonSerializerSettings(), config);
initMsg = String.Format("{0} UseJsonFormat={1}", initMsg, useJsonFormat);
Expand Down
32 changes: 32 additions & 0 deletions src/OrleansTestingHost/Extensions/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;

namespace Orleans.Runtime.Configuration
{
public static class ConfigurationExtensions
{
/// <summary>
/// Applies the specified config change defined by <paramref name="nodeConfigUpdater"/> to
/// <see cref="ClusterConfiguration.Defaults"/> and all the node configurations currently
/// defined in <see cref="ClusterConfiguration.Overrides"/>.
/// </summary>
/// <param name="config">The cluster configuration object to add provider to.</param>
/// <param name="nodeConfigUpdater">The function to apply to each node configuration.</param>
public static void ApplyToAllNodes(this ClusterConfiguration config, Action<NodeConfiguration> nodeConfigUpdater)
{
foreach (var nodeConfiguration in config.GetDefinedNodeConfigurations())
{
nodeConfigUpdater.Invoke(nodeConfiguration);
}
}

private static IEnumerable<NodeConfiguration> GetDefinedNodeConfigurations(this ClusterConfiguration config)
{
yield return config.Defaults;
foreach (var nodeConfiguration in config.Overrides.Values)
{
yield return nodeConfiguration;
}
}
}
}
1 change: 1 addition & 0 deletions src/OrleansTestingHost/OrleansTestingHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<Compile Include="..\Build\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Extensions\ConfigurationExtensions.cs" />
<Compile Include="Extensions\TestConfigurationExtensions.cs" />
<Compile Include="OrleansTestSecrets.cs" />
<Compile Include="OrleansTestStorageKey.cs" />
Expand Down
8 changes: 2 additions & 6 deletions test/Tester/GenericGrainsInAzureStorageTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Orleans.Runtime.Configuration;
using Orleans.TestingHost;
using UnitTests.GrainInterfaces;
using UnitTests.Tester;
Expand All @@ -21,11 +21,7 @@ protected override TestingSiloHost CreateClusterHost()
StartSecondary = false,
AdjustConfig = config =>
{
const string myProviderFullTypeName = "Orleans.Storage.AzureTableStorage";
const string myProviderName = "AzureStore";
var properties = new Dictionary<string, string>();
properties.Add("DataConnectionString", "UseDevelopmentStorage=true");
config.Globals.RegisterStorageProvider(myProviderFullTypeName, myProviderName, properties);
config.AddAzureTableStorageProvider("AzureStore", StorageTestConstants.DataConnectionString);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,7 @@ private static void AdjustConfig(ClusterConfiguration config)
{
// register stream provider
config.Globals.RegisterStreamProvider<EventHubStreamProvider>(StreamProviderName, BuildProviderSettings());
config.Globals.RegisterStorageProvider<AzureTableStorage>(
ImplicitSubscription_RecoverableStream_CollectorGrain.StorageProviderName,
new Dictionary<string, string>
{
{"DataConnectionString", StorageTestConstants.DataConnectionString}
});
config.AddAzureTableStorageProvider(ImplicitSubscription_RecoverableStream_CollectorGrain.StorageProviderName, StorageTestConstants.DataConnectionString);

// Make sure a node config exist for each silo in the cluster.
// This is required for the DynamicClusterConfigDeploymentBalancer to properly balance queues.
Expand Down

0 comments on commit 1077f5e

Please sign in to comment.