Thursday, February 12, 2015

"From complete chaos to Octopus Deploy" Part 4: Required application changes

Introduction to this blog series

How do you automate the non-existing deployment routines of an organization with over 100 different customers, each having their own environments? How do you convince the leaders, developers and customers to give you the resources needed in order to automate everything?  Is it really possible to introduce a routine that works for everyone?
Part 1: The trigger of change 
Part 2: Where to begin? 
Part 3: The basic concepts of Octopus Deploy
Part 4: Required application changes
Part 5: The deployment process
Part 6: The hosting vendors
Part 7: Load balancing
Part 8: Lessons learned and useful resources

Required application changes

While I tried to minimize the amount of change needed for our applications, there were some changes we could not avoid. I won't go into detail on all of them, but I would like to outline them briefly in case you come across similar needs.

Installing OctoPack

As decribed in the Octopus documentation, the OctoPack NuGet builds Octopus Deploy-compatible NuGet packages from your projects. These packages are pushed to Octopus Deploy, and Octopus runs config transforms before deploying the package to the chosen environment. We added the OctoPack NuGet to all our projects, in addition to a default .nuspec file to supply some basic information about out applications. Having installed the Octopus TeamCity plugin, the plugin handles the rest: Running OctoPack for creating the packages.

Config transformations

Previous to Octopus Deploy, we ran all our config transformations on build. This is a problem as you will never have the exact same assemblies in any environment. Imagine I deployed code to my Test environment, in order to deploy the same code to my Production environment I would have to rebuild my code as the config transformations were run on build. Rebuilding the code would generate a new set of assemblies, which in theory would contain the same code as the assemblies deployed to Test, but they would not be identical, or the exact same files. 
After introducing Octopus Deploy, the config transformations would no longer be run on build. Instead the NuGet generated by OctoPack would contain all the config transformation files, and we would configure Octopus to run the config transformations:
In order to change when the config transformations were run, we needed to make a couple of changes to our applications: 

- Disable transformations on build by setting TransformOnBuild to false in our .csproj files
- Set the Build Action of all config transform files to Build Action = Content so that they would be included in the NuGet package generated by OctoPack

Deploy items

Our last challenge had to do with static files that should reside in the application root folder and that vary from environment to environment, but cannot be transformed. Some examples are robots.txt files (which you could transform if you converted to an xml, but we wanted to avoid that), and license.txt files.

Our solution was to add folders called DeployItems. and add the static files that belong to the given environment to this folder:

We then added a script module in Octopus that all the projects would reference. This script module contained a simple deploy script, that would look for a DeployItems folder postfixed with the environment name, and if found it would copy all the files in the folder to the application root folder:

function Move-DeployItems() {
    Write-Host "Checking if there are any DeployItems to move..."

    if(Test-Path -Path .\Configs\DeployItems.$OctopusEnvironmentName\){
        Write-Host "Moving deploy items from Configs\DeployItems.$OctopusEnvironmentName to root folder"
        Move-Item .\Configs\DeployItems.$OctopusEnvironmentName\* .\ -Force
    }
    
    if(Test-Path -Path .\Configs\DeployItems.$OctopusEnvironmentName.$OctopusMachineName\){
        Write-Host "Moving deploy items from Configs\DeployItems.$OctopusEnvironmentName.$OctopusMachineName to root folder"
        Move-Item .\Configs\DeployItems.$OctopusEnvironmentName.$OctopusMachineName\* .\ -Force
    }

    Write-Host "Deleting deploy items folders"
    Get-ChildItem .\Configs\DeployItems.* -Recurse | foreach ($_) {Remove-Item $_.fullname -Force -Recurse}
    Remove-Item .\Configs\DeployItems.* -Recurse
}

Notice that for load balanced enviroments, you could add a DeployItems.. folder if the files were different for all machines as well as all environments. To finish this off the DeployItems folders are deleted after the files are copied. 

With these minor changes: Adding OctoPack, disabling config transforms on build, and adding DeployItems folders for copying static files, our applications were all Octopus Deploy-compatible. Stay tuned for tomorrows post: The deployment process

No comments:

Post a Comment