Running an Azure Cloud Service without an Emulator

Running and debugging complex code in an Azure deployment environment frequently requires implies a long turn around time when running on the Azure fabric. Building, packing, uploading and startup all take time. Running the code locally on an Azure compute emulator is a little better once you wait for the emulator to spin up.

Often the experiment...build...run process doesn't really need to use an emulated environment and the grand old tradition of using a temporary console application to run a code snippet still applies. But for integration level issues, what if you want to replicate as far as possible the environment - database, REST service calls, configuration - that you see in Azure?

Blocking?

There are two blocking issues that can prevent you from using a standard console application to host your application.

  • Your worker role derived from Microsoft.WindowsAzure.ServiceRuntime.RoleEntryPoint
  • RoleEnvironment settings.

RoleEntryPoint

The worker role is often not too much of a problem. It should always be akin to the code in a console program's Main method; as small as possible and really just plugging together blocks in the right configuration as pressing "go".

RoleEnvironment

The RoleEnvironment can require a little more work. It's a static class so it's reach into a codebase can be pervasive. We can help ourselves a little by hiding the static-ness behind an interface IRoleSettings, say, and passing that into constructors.


public interface IRoleSettings
{
    string ReadValue(string key);
}

When can then create an AzureRoleSettings implementation that delegates to RoleEnvironment for the deployed scenario.


/// <summary>
/// Deployed implementation
/// </summary>
public class AzureRoleSettings : IRoleSettings
{
    public string ReadValue(string key)
    {
        return RoleEnvironment.GetConfigurationSettingValue(key);
    }
}

For unit testing, we can mock the interface or create a StubRoleSettings implementation that uses a Dictionary to store name value pairs.


/// <summary>
/// Unit test version (or mock IRoleSettings)
/// </summary>
public class StubRoleSettings : IRoleSettings
{
    public Dictionary<string, string> nameValuePairs = new Dictionary<string, string>();

    public string ReadValue(string key)
    {
        return this.nameValuePairs[key];
    }
}

For integration testing or debugging subtle issues requiring real world values we would really like to be able to read the cscfg and pull out values for an individual role. Since we already have the IRoleSettings interface, we can implement an IntegrationRoleEnviroment class and use XPath to build our list of name value pairs.


/// <summary>
/// Integration test version, reads from local cscfg
/// </summary>
public class LocalCloudConfigRoleSettings : IRoleSettings
{
    private Dictionary<string, string> nameValuePairs = new Dictionary<string, string>();

    public static LocalCloudConfigRoleSettings FromCloudConfig(string path, string roleName)
    {
        LocalCloudConfigRoleSettings settings = new LocalCloudConfigRoleSettings();

        XElement doc = XElement.Load(path);

        const string RoleElement = "Role";
        const string NameAttribute = "name";

        // namespace for cscfgs
        XNamespace scns = "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration";

        // find the correct role
        XElement requiredRole = (from role in doc.Descendants(scns + RoleElement)
                                 where (string)role.Attribute(NameAttribute) == roleName
                                 select role).FirstOrDefault();

        if (requiredRole != null)
        {
            const string SettingElement = "Setting";
            const string ValueAttribute = "value";

            // import each name value pair
            foreach (XElement setting in requiredRole.Descendants(scns + SettingElement))
            {
                XAttribute name = setting.Attributes(NameAttribute).FirstOrDefault();
                XAttribute value = setting.Attributes(ValueAttribute).FirstOrDefault();

                if (name != null && value != null)
                {
                    settings.nameValuePairs.Add(name.Value, value.Value);
                }
            }
        }

        return settings;
    }

    public string ReadValue(string key)
    {
        return this.nameValuePairs[key];
    }
}