Auto updating webrole

by ingvar 20. februar 2012 21:17

While working on a cool Windows Azure deployment package for Composite C1 I did a lot of deployments. The stuff I did, like reconfiguring the IIS, needed testing on an actual Azure hosted service and not just the emulator. Always trying to optimize, I thought if ways to get around all this redeploying and came up with the idea of making it possible to change the WebRole behavior at run time. Then I could just “inject” a new WebRole and start testing without having to wait on a new deployment. After some fiddling around I found a really nice solution and will present it here!

Solution description

The solution I came up with was dynamically loading an assembly into a newly created AppDomain and calling methods within on an instance of a class in the assembly.
This is a fairly simple task and all the code needed is shown here:

/* Creating domain */
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.PrivateBinPath = folder;
domainSetup.ApplicationBase = folder;
AppDomain appDomain = AppDomain.CreateDomain(“MyAssembly”, null, domainSetup);

/* Creating remove proxy object */
IDynamicWebRole dynamicWebRole =
   (IDynamicWebRole)appDomain.CreateInstanceAndUnwrap(
      “MyAssembly”, 
      “MyAssembly.MyDynamicWebRole, MyAssembly”);

/* Calling method */
dynamicWebRole.Run();

 

Common interface: IDynamicWebRole

The code for the IDynamicWebRole interface is in its own little assembly. And the code shown here and can be changed as you wish.

interface IDynamicWebRole
{
    void Run();
}

There is no actual need for an interface, but both the Azure WebRole project and the assembly project that contains the actuall IDynamicWebRole implementation needs to share a type. So that’s why I created this interface and put it in its own assembly. The assemblies/projects in play is shown in this figure:

Now its time to look at the more interesting code in the WebRole. It’s where all the magic is going on!

The WebRole implementation

The WebRole implementation is rather complex. The WebRole needs to periodicly look for new versions of the IDynamicWebRole implementation and when there is a new version, download it, start new AppDomain and create a remote instance of the IDynamicWebRole implementation. Here is all the code for the WebRole. After the code, I will go into more detail on how this works.

public class WebRole : RoleEntryPoint
{
    /* Initializes these */
    private readonly CloudBlobClient _client;
    private readonly string _assemblyBlobPath = 
        "mycontainer/MyAssembly.dll";
    private readonly string _dynamicWebRoleHandlerTypeFullName = 
        "MyAssembly.MyDynamicWebRole, MyAssembly";

    private AppDomain _appDomain = null;
    private IDynamicWebRole _dynamicWebRole;

    private volatile bool _keepRunning = true;
    private DateTime _lastModifiedUtc = DateTime.MinValue;


    public override void Run()
    {
       int tempFolderCounter = 0;

       while (_keepRunning)
       {
           CloudBlob assemblyBlob = 
               _client.GetBlobReference(_assemblyBlobPath);
           DateTime lastModified = assemblyBlob.Properties.LastModifiedUtc;

           if (lastModified > _lastModifiedUtc)
           {
               /* Stop running appdomain */
               if (_appDomain != null)
               {
                   AppDomain.Unload(_appDomain);
                   _appDomain = null;
               }

               /* Create temp folder */
               string folder = Path.Combine(
                   AppDomain.CurrentDomain.BaseDirectory, 
                   tempFolderCounter.ToString());
               tempFolderCounter++;
               Directory.CreateDirectory(folder);

               /* Copy needed assemblies to the folder */
               File.Copy("DynamicWebRole.dll", 
                   Path.Combine(folder, "DynamicWebRole.dll"), true);

               File.Copy("Microsoft.WindowsAzure.StorageClient.dll", 
                   Path.Combine(folder, 
                       "Microsoft.WindowsAzure.StorageClient.dll"), true);

               /* Download from blob */
               string filename = 
                   _assemblyBlobPath.Remove(0, _assemblyBlobPath.LastIndexOf('/') + 1);
               string localPath = Path.Combine(folder, filename);
               assemblyBlob.DownloadToFile(localPath);

               string assemblyFileName = 
                   Path.GetFileNameWithoutExtension(localPath);

               /* Create new appdomain */
               AppDomainSetup domainSetup = new AppDomainSetup();
               domainSetup.PrivateBinPath = folder;
               domainSetup.ApplicationBase = folder;
               _appDomain = 
                  AppDomain.CreateDomain(assemblyFileName, null, domainSetup);

               /* Create IDynamicWebRole proxy instance for remoting */
               _dynamicWebRole = 
                   (IDynamicWebRole)_appDomain.CreateInstanceAndUnwrap(
                       assemblyFileName, _dynamicWebRoleHandlerTypeFullName);
                       
               /* Start the dynamic webrole in other thread */
               /* so we can continue testing for new assebmlies */
               /* Thread will end when the appdomain is unloaded by us */
               new Thread(() => _dynamicWebRole.Run()).Start();

               _lastModifiedUtc = lastModified;
           }
           
           Thread.Sleep(30 * 1000);
       }
    }


    public override void OnStop()
    {
       _keepRunning = false;
    }
}

I have omitted all the exception handling to make the code more readable an easier to understand.

IDynamicWebRole implementation

The last thing we need is to implement the IDynamicWebRole interface and have it in its own assembly. There is two important things when implementing the interface for the remoting to work and that is implementing MarshalByRefObject class and overriding the InitializeLifetimeService method. This is shown in the following code:

public class MyDynamicWebRole : MarshalByRefObject, IDynamicWebRole
{
    public void Run()
    {
       /* Put your webrole implementation here */
    }

    public override object InitializeLifetimeService()
    {
       /* This is needed so the proxy dont get recycled */
       return null;
    }
}

Thats all there is to it, enjoy! :)

Tags:

.NET | Azure | C#

Bitbucket setup on OSX Lion using Mercurial

by ingvar 12. februar 2012 08:05

Here is a fast guide to getting Mercurial and bitbucket working from the terminal in OSX Lion, enjoy :)

1. Download and install Mercurial

Download binaries from http://mercurial.selenic.com/ and install.

2. Creating Mercurial configuration

Create a .hgrc filer in your home folder: ~/.hcrg and add the following lines. Change the name and email to your name and email used ont butbucket.

[ui]
username = Jon Doe <jon@doe.com>
ssh = ssh -C

3. Configure SSH 

3.1: Set up your default identity by issuing the following command and entering a proper password

ssh-keygen

3.2: Copy the public key to your clipboard using the following command

cat ~/.ssh/id_rsa.pub | pbcopy

3.3 Add public key to bitbucket by browsing to Account -> SSH Keys and past the public key in the textbox left to the 'Add key' button. Then click the 'Add key' button.

4. All done!

Rember to use the SSH URL when cloning the repository and not the HTTPS url.

Tags:

About the author

Martin Ingvar Kofoed Jensen

Architect and Senior Developer at Composite on the open source project Composite C1 - C#/4.0, LINQ, Azure, Parallel and much more!

Follow me on Twitter

Read more about me here.

Read press and buzz about my work and me here.

Stack Overflow

Month List