Dec 22
Are focused tests really worth it?
icon1 Darrell Mozingo | icon2 Testing | icon4 December 22nd, 2010| icon3No Comments »

We recently had the requirement to start filling in fillable PDF’s. The fields in fillable PDF’s are just string names, with text boxes that get string values, check boxes that have special values, etc. I decided to create model classes to represent each PDF, then mapping classes to map each of the model’s properties to a field in the PDF. I ended up with something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PdfModel
{
	public string Name { get; set; }
	public Money Amount { get; set; }
	public bool Sent { get; set; }
	public string StateAbbreviation { get; set; }
}
 
public class PdfModelMapping : PdfMappingBase<PdfModel>
{
	protected override void CreateMap()
	{
		Map(x => x.Name).To("name_field");
		Map(x => x.Amount, DollarsTo("dollar_field").CentsTo("cents_field"));
		Map(x => x.Set).To("sent_field");
 
		Map(x => x.StateAbbreviation, m =>
						{
							m.Map(x => x.ToCharArray()[0]).To("state_first_letter_field");
							m.Map(x => x.ToCharArray()[1]).To("state_second_letter_field");
						});
	}
}

Any similarity to a popular open source tool is completely coincidental. Hah! Anyway, it’s working well so far. When I set out to write this, I started with a single fixture for the PdfMappingBase class above. I made a small mapping for a single property, then another one for a check box, then another one for a multiple field mapping, etc. I found that while I ended up with around 10 supporting classes, every line of code in them existed to fulfill one of those easy tests in the mapping base fixture.

So I test drove the overall thing, but not each piece. There’s no tests for the individual classes that make up this mapping system, but there’s also not a single line not covered by a test (either technically by just hitting it, or meaningfully with a test to explain why it’s there). Is that wrong? I’m thinking no.

Developing this seemed very natural. I created a simple test that showed how I wanted the end API to look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[TestFixture]
public class When_mapping_a_single_text_box_property : SpecBase
{
	IEnumerable<PdfField> _fieldsFromMapping;
	readonly TestPdfModel _model = new TestPdfModel { Name = "name_value" };
 
	protected override void because()
	{
		_fieldsFromMapping = new SinglePropertyPdfMapping().GetAllFieldsFrom(_model);
	}
 
	[Test]
	public void Should_only_have_one_field_mapping()
	{
		_fieldsFromMapping.Count().ShouldEqual(1);
	}
 
	[Test]
	public void Should_set_the_field_name_based_on_the_mapping_definition()
	{
		_fieldsFromMapping.First().FieldName.ShouldEqual("field_name");
	}
 
	[Test]
	public void Should_set_the_value_from_the_model()
	{
		_fieldsFromMapping.First().TextBoxValue.ShouldEqual("name_value");
	}
 
	private class SinglePropertyPdfMapping : PdfMappingBase<TestPdfModel>
	{
		protected override void CreateMap()
		{
			Map(x => x.Name).To("field_name");
		}
	}
}

Then I just created the bare minimum to get it compiling & passing, refactored, and moved on to the next part of the API. Rinse & repeat. Again, I test drove the whole shebang in a top-down way, but not the individual classes themselves. This whole thing isn’t going out to any resources, so it runs fast and all that jive. The only draw back I can see if being hard to pin down problems in the future - having to navigate through a dozen or so classes to find why a test is failing probably won’t be fun. On the upside, I’ve found refactoring on the whole much easier, as the tests only look at the entry point to the whole API. I can change how the classes interact through each of their own public interfaces pretty easy, without having to update tests that may be looking at that one specific class.

Thoughts? I know taken too far this is a bad idea, but what about this situation? Think I should add tests for each of the supporting classes?

Dec 16
Introducing Taskie
icon1 Darrell Mozingo | icon2 Taskie | icon4 December 16th, 2010| icon35 Comments »

A little over a year ago I offhandedly mentioned the scheduled task program I wrote for one our products at work. Well, I’m finally releasing a stripped down version as open source.

Taskie is a super simple way to create and manage .NET scheduled task applications, built with dependency injection at its core.

Why?

You’re always going to need to do some kind of back-end processing in your apps, and you basically have two choices for them: a command line app, or a service. When it came time for that decision on our current project, we decided we never much cared for the deployment story with services (even with the awesomeness that Topshelf brings to the tableshelf), and our server geeks didn’t like them much either for whatever reason. We’re all used to console apps though, and they work, so we stuck with them.

But adding a new projecting to the solution for each scheduled task exe we needed? Parsing command line arguments? Not having dependency injection available? Having to deploy all that junk? No thanks!

So I whipped up Taskie. It handles all the boiler plate crud and eases deployment for us and the server geeks. Once you have it setup, whenever you need a new scheduled task you just slap in a class, implement a simple interface, and Taskie handles the rest.

Getting Started

UPDATE: The first version I published wouldn’t work if you were using StructureMap (as Taskie uses that internally). The assemblies linked below are now updated to work correctly in that situation.

  1. Download the Taskie assemblies
  2. Add a console application to your solution
  3. Add a reference to Taskie.dll in the console application project, and set it to build against the full .NET 4.0 Framework (not the default Client Profile)
  4. Implement Taskie.ITaskieServiceLocator in your application, using your dependency injection tool of choice. These methods should be single line implementations.
    1
    2
    3
    4
    5
    
    public interface ITaskieServiceLocator
    {
    	INSTANCE GetInstance<INSTANCE>();
    	IEnumerable<INSTANCE> GetAllInstances<INSTANCE>();
    }
  5. Inside Program.cs, within your console application, initialize your dependency injection tool however you normally would and call TaskieRunner.RunWith(), passing the command line arguments and an instance of your implementation of IServiceLocator, like this:
    1
    2
    3
    4
    5
    
    public static void Main(string[] args)
    {
    	IoC.Bootstrap();
    	TaskieRunner.RunWith(args, new ServiceLocator());
    }
  6. Add a class that implements Taskie.ITask somewhere in your main project, name it “FooTask” (where Foo is whatever you want, but it must end with Task), and make sure your dependency injection tool knows about it (either through auto discovery or explicity registered):
    1
    2
    3
    4
    
    public interface ITask
    {
    	void Run();
    }

That’s it! Taskie is all setup and ready to roll. Running your console application with no command line arguments will show a usage screen listing any tasks that ready to run. Run the executable with “/run Foo” and it’ll run whatever you have in the Run method on your FooTask class.

Taskie Usage Screen

A few optional things you can do:

  1. Tag your task class with the TaskDescription attribute, providing it a string description to display on the usage screen (as seen above)
  2. Implement Taskie.ITaskieApplication to run any code before and after Taskie does its thing (such as setting up your NHibernate session)
    1
    2
    3
    4
    5
    
    public interface ITaskieApplication
    {
    	void Startup();
    	void Shutdown();
    }

Future Plans

A few of the things I’m thinking about for the future:

  • NuGet package!
  • ILMerge everything into one assembly file
  • Create a way to schedule tasks on the server (either a fluent interface or through XML/text files), then have an MSBuild/NAnt/PowerShell/rake package that’ll remotely set those scheduled tasks up
  • Ability to log when tasks run & finish, along with a built-in task to ensure tasks are running when they should and can report when they don’t (using the definitions mentioned above)
  • Error reporting from tasks - emails, a pluggable interface, etc.
  • An ITransactionalTask interface that provides a roll back method to cleanly implement that functionality when needed

Check out the source code on GitHub. A sample application is included.

This is my first open source project and first experience with Git, so please go easy on me :)

If you have any suggestions for anything (especially on how I can ease the getting started process), I’m all ears!

Dec 3

The first post gave a quick overview of what our deployment script does and why you’d want one, the second post went over pre-deployment steps, and the third post in this series covered the actual site’s deployment. This post will go over a few of the post-deployment steps we take after publishing our site. Like the last posts, most all of this code will probably be pretty self explanatory.

Preloading

We make heavy use of StructureMap, NHibernate (w/Fluent NHibernate), and AutoMapper in our system, and those guys have some heavy reflection startup costs. Since it’s all done when the app domain starts, we hit each server in our farm to “pre-load” the site for us because that first visit takes a good 30-40 seconds because of those tools.

Since the servers are in a farm, we can’t just go to the site’s URL as we’d only get one box - even multiple loads aren’t guaranteed to move you around to them all. To make sure we’re looking at each server, we fiddle with the build server’s hosts file and point it at each web server. We don’t do parallel builds on our build server, so we thankfully don’t have any issues with other build scripts getting tripped up, but you may want to consider that if it’s applicable to your situation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
properties {
	$hosts_file = "C:\Windows\System32\drivers\etc\hosts"
 
	$servers_production = @( "server1", "server2" )
	$servers_production_ip = @{ "server1" = "192.168.1.1"; "server2" = "192.168.1.2" }
}
 
function setup_hosts_file_for($server, $url)
{
	$server_ip = $servers_production_ip[$server]
 
	echo "Setting hosts file to use $server_ip ($server) for $url."
 
	"$server_ip $url" | Out-File $hosts_file -Encoding Ascii
}
 
function remove_hosts_file_entries
{
	echo "Removing all hosts file entries and reverting to a clean file."
 
	"127.0.0.1 localhost" | Out-File $hosts_file -Encoding Ascii
}
 
function make_sure_we_are_pointing_at($server, $url)
{
	$expected_server_ip = $servers_production_ip[$server]
 
	$ping_output = & ping -n 1 $url
	$ip_pinged = ($ping_output | Select-String "\[(.*)\]" | Select -ExpandProperty Matches).Groups[1].Value
 
	if ($ip_pinged -ne $expected_server_ip)
	{
		throw "The site's IP is supposed to be $expected_server_ip, but it's $ip_pinged (for $url). Hosts file problem?"
	}
 
	echo "Correctly pointing at $ip_pinged for $url."
}
 
function stop_dns_caching
{
	& net stop dnscache
}
 
function start_dns_caching
{
	& net start dnscache
}

The hosts file allows you to point any request for, say, www.asdf.com on your machine to whatever IP you want. So if you wanted to preload www.asdf.com for server1, you can put “192.168.1.1 www.asdf.com” in your hosts file, and you’ll always hit that machine. Your load balancing setup might not allow this though. There’s also a method that’ll ping the given URL to make sure it’s going to the proper server, throwing up if it isn’t. The last two methods start/stop the DNS Caching service in Windows, just to help make sure we’re looking at the correct IP for a given URL.

With that setup, we can easily manipulate IE through COM to pull up the site:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
properties {
	$live_site_text_in_title = "Our cool site"
	$times_to_try_preloading_sites = 50
}
 
function fire_up_ie
{
	return New-Object -Com InternetExplorer.Application
}
 
function preload_url_on_server($server, $url)
{
	setup_hosts_file_for $server $url
	make_sure_we_are_pointing_at $server $url
 
	$current_attempt_count = 0
	$3_seconds = 3
 
	$ie = fire_up_ie
	$ie.navigate($url)
 
	echo "Pulling up $url in the browser."
 
	while ($current_attempt_count -lt $times_to_try_preloading_sites)
	{
		pause_for $3_seconds
 
		$document = $ie.document
 
		if ($document -ne $null -and $document.readyState -eq "Complete" -and $document.title -match $live_site_text_in_title)
		{
			$time_taken = ($current_attempt_count + 1) * $3_seconds
			echo "Preloaded $url on $server in about $time_taken seconds."
 
			break
		}
 
		$current_attempt_count++
	}
 
	$ie.quit()
 
	if ($current_attempt_count -ge $times_to_try_preloading_sites)
	{
		throw "$url (on $server) couldn't be preloaded after a pretty long ass wait. WTF?"
	}
}

Working with IE’s COM interface is pretty painless in PowerShell. Dynamic languages FTW, aye? We just fire up IE, browse to the URL (which should be pointing to the given server only), and keep checking on IE’s progress until the page is fully loaded the title contains some piece of text we expected it to. Simple and to the point.

The first snippet in Part 3 of this series showed how we deployed the site. You can see there where we temporarily stop the DNS Caching service, then pre-load the site on each server are deploying to it, then reset the hosts file and start the DNS Caching service again.

Testing Error Email Generation

We have some basic code to email exceptions out if our app hits an exception. Nothing fancy. To test our error emails are getting sent OK, I created an obscure URL in the application that’ll just generate a TestErrorEmailException. When our error handler sees that exception, all it does it send the generated error email to a buildserver@domain.com address rather than the normal one. The build script then logs into it’s special GMail accont and checks for the email. This is bar far the chunckiest part of the build script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
properties {
	$email_url = "mail.ourdomain.com"
	$error_generation_path = "/SomeObscurePath/GenerateTestErrorEmail/?subject="
	$max_email_check_attemps = 100
}
 
function wait_for_browser_to_finish($ie)
{
	while ($ie.busy -eq $true) {
		pause_for 1 #second
	}
}
 
function generate_test_error_emails_on($server, $base_url, $error_email_subject)
{
	setup_hosts_file_for $server $base_url
	make_sure_we_are_pointing_at $server $base_url
 
	$error_url = $base_url + $error_generation_path
	$full_error_url = $error_url + $error_email_subject
 
	$ie = fire_up_ie
	$ie.navigate($full_error_url)
 
	echo "Generating test error email from $full_error_url."
 
	wait_for_browser_to_finish $ie
 
	$ie.quit()
}
 
function ensure_error_emails_are_working_on($server, $base_url)
{
	echo "Ensuring error emails are getting sent out correctly on $server."
 
	$current_datetime = Get-Date -Format MM_dd_yyyy-hh_mm_tt
	$error_email_subject = "Error_" + $server + "_$current_datetime"
 
	generate_test_error_emails_on $server $base_url $error_email_subject
	check_email_was_sent $error_email_subject
}
 
function check_email_was_sent($expected_email_subject)
{
	echo "Pulling up $email_url in the browser."
 
	$ie = fire_up_ie
	$ie.navigate($email_url )
	wait_for_browser_to_finish $ie
 
	logout_of_email $ie
 
	echo "Logging in to email."
 
	$ie.document.getElementById("email").value = $security_user
	$ie.document.getElementById("passwd").value = $security_password
	$ie.document.getElementById("signin").click()
	wait_for_browser_to_finish $ie
 
	echo "Looking for test error email."
 
	$test_error_email = $null
 
	for ($i = 1; $i -le $max_email_check_attemps; $i++)
	{
		echo "Attempt #$i checking for the test error email."
 
		$test_error_email = get_link_containing_text $ie $expected_email_subject
 
		if ($test_error_email -ne $null)
		{
			echo "Found the test error email."
			break
		}
 
		pause_for 10 #seconds
 
		echo "Refreshing the page after a pause."
		click_link_with_text $ie "Refresh"
	}
 
	if ($test_error_email -eq $null)
	{
		$ie.quit()
		throw "Test error email was never received after $max_email_check_attemps attempts. Problem?"
	}
 
	echo "Pulling up the test error email."
 
	$ie.navigate($test_error_email.href)
	wait_for_browser_to_finish $ie
 
	echo "Deleting test error email."
	click_link_with_text $ie "Delete"
 
	logout_of_email $ie
 
	$ie.quit()
}
 
function logout_of_email($ie)
{
	$signout_link = get_link_with_text $ie "Sign out"
 
	if ($signout_link -ne $null)
	{
		echo "Signing out of email."
		$ie.navigate($signout_link.href)
 
		wait_for_browser_to_finish $ie
	}
}
 
function click_link_with_text($ie, $text)
{
	$link = get_link_with_text $ie $text
	$there_are_multiple_links_with_that_text = ($link.length -gt 1)
 
	if ($there_are_multiple_links_with_that_text)
	{
		$ie.navigate($link[0].href)
	}
	else
	{
		$ie.navigate($link.href)
	}
 
	wait_for_browser_to_finish $ie
}
 
function get_link_with_text($ie, $text)
{
	return $ie.document.getElementsByTagName("a") | where { $_.innerText -eq $text }
}
 
function get_link_containing_text($ie, $text)
{
	return $ie.document.getElementsByTagName("a") | where { $_.innerText -match $text }
}

It seriously looks worse than it really is, and most of it is due to navigating around GMail’s interface. So we hit the obscure URL in our app, pass it a subject line for the error email, wait a bit, then log into GMail and check for an email with that same subject line. If we don’t find the email after a waiting period, we blow up the script. Simple as that.

If you know an easier way to do this, I’m all ears!

Conclusion

The two biggest things we do after deploying our site is, for each individual server in the farm, load it up so all the first time reflection stuff can get taken care of and make sure any errors on the site are getting emailed out correctly. While controlling IE through its COM interface is a lot cleaner and easier with PowerShell, there’s still some code for navigating around GMail’s site. Obviously if you use a different setup for your email, you’ll either have to control a different app or access the SMTP server directly.

Unfortunately, the biggest piece for both of these things being helpful is if you can navigate to each server in the farm. If your network setup prevents that, it’s not going to do you much good unless you keep clearing your cookies and revisiting the site a bunch of times in hopes you’ll get each server, or something crazy like that.

So while most of this code is straight forward, I hope it’ll give you a starting point for your deployment script. Like I said in the beginning: it’s a bit painful to initially setup (both creating it and testing it), but we’ve found huge value from having it in place. It’s obviously not as easy as Capistrano, but, meh, it works. Another option for .NET is Web Deploy, a relatively new tool from Microsoft. I haven’t had time to get too deep into it, but it may help for your situation.

Good luck!

Nov 24

In the first post I gave a quick overview of what our deployment script does and why you’d want one, then the second post went over pre-deployment steps. This post will go over the actual deployment steps we take to publish our site. Like the last post, most all of this code will probably be pretty self explanatory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function deploy_and_prime_site
{
	modify_web_config_for_production
	precompile_site
 
	archive_site
	delete_extra_live_site_backups
 
	try
	{
		stop_dns_caching
 
		foreach ($server in $servers_production)
		{
			deploy_site_to $server
			preload_site_on $server
		}
 
		foreach ($server in $servers_production)
		{
			ensure_error_emails_are_working_on $server $live_url
		}
	}
	finally
	{
		remove_hosts_file_entries
		start_dns_caching
	}
}

This is the function the build target actually calls into. The part you’ll care about here is where it loops through the known production servers and deploys the site to each one in tern. The “preloading” of the site, checking for functioning error emails, and DNS caching stuff is some of the post-deployment steps we take, which I’ll discuss in the next post.

IIS Remote Control

Here’s how we control IIS remotely (this is IIS7 on Windows 2008 R2 - not sure how much changes for different versions):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function execute_on_server($target_server, [scriptblock]$script_block)
{
	$secure_password = ConvertTo-SecureString $security_password -AsPlainText -Force
	$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $security_full_user, $secure_password
 
	Invoke-Command -ComputerName $target_server -Credential $credential -ScriptBlock $script_block
}
 
function stop_iis_on($target_server)
{
	echo "Stopping IIS service on $target_server..."
 
	execute_on_server $target_server { & iisreset /stop }
}
 
function start_iis_on($target_server)
{
	echo "Starting IIS service on $target_server..."
 
	execute_on_server $target_server { & iisreset /start }
}

The secret sauce to getting this to work is the execute_on_server function. The actual stop & start methods just execute standard iisreset commands (which is a built-in command line tool w/IIS). So the top function converts our plain text server username & passwords in the build script into a SecureStringPSCredential object. Not the most secure way to do this, I’m sure (hence the -Force parameter), but it’s working for us. After connecting to the remote machine, it executes the given script block with those credentials (like the execute_with_secure_share function from the last post). In order to make this work though, you’ll need to give some lovin’ on your build server and web servers:

  • Make sure all boxes have at least PowerShell 2.0 with WinRM 2.0 (which is what allows the remote machine command execution)
  • On each web server, you’ll need to run this one time command from a PowerShell prompt: Enable-PSRemoting

Deployment

With that out of the way, the actual deployment part is pretty easy - it’s just copying files after all:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
properties {
	$siteWebFolder_name = $solution_name
 
	$ident_file = "Content\ident.txt"
}
 
function pause_for($seconds)
{
	sleep -s $seconds
}
 
function deploy_site_to($server)
{
	echo "*** Beginning site deployment to $server."
 
	$compiled_site = "$compiled_site\*"
	$web_share = "\\$server\$share_web"
	$live_site_path = "$web_share\$siteWebFolder_name"
 
	stop_iis_on $server
 
	pause_for 10 #seconds, to give IIS time to release file handles.
 
	execute_with_secure_share $web_share {
		echo "Deleting the existing site files on $server ($live_site_path )."
		delete_directory_with_errors "$live_site_path \*"
 
		echo "Copying the new site files (from $compiled_site) to $server."
		copy_directory $compiled_site $live_site_path 
 
		echo "Creating ident file at $live_site_path."
		"$server" > "$live_site_path\$ident_file"
	}
 
	start_iis_on $server
}

Stop IIS, give it a few seconds, copy files, start IIS. Like I said - simple. If your situation can’t allow this for some reason (perhaps you have a more complicated load balancing scheme or whatever), you can expand as needed. We actually deploy several sites and a few console apps at the same time so everything’s in sync. The ident file is a simple way for us to find out which server a user’s on for troubleshooting purposes. We can navigate to the url + /Content/ident.txt and it’ll have the server’s name.

Conclusion

Other than the actual remote manipulation of the servers, which we keep to a pretty minimum IIS start & stop, there’s not much to this part of the build either. This code provides a good jumping off point for customization to your setup, as well as some helper methods you can hopefully make use of. The next post will wrap up this series by showing some of the post-deployment steps we take.

Nov 18
How *not* to hash passwords
icon1 Darrell Mozingo | icon2 Misc. | icon4 November 18th, 2010| icon3No Comments »

We were stupid back in the day (OK, a year or two, but who’s counting?). When we started our latest project it was a given that we’d be hashing passwords for storage. The most obvious and easiest way to do it was the good ‘ol (password + hash).GetHashCode(). Done and done. We moved on to the next feature and never gave it a second thought.

As it turns out though, using GetHashCode() for password hashing purposes is, well, pretty stupid and irresponsible. GetHashCode() was never intended to be stable across .NET versions or even architectures (x86 vs x64), and apparently the framework spec documents call this out. In fact, its results have changed slightly between .NET 3.5 and 4.0, which is what we were just upgrading to when I noticed this. Similar changes aparently occurred between 1.1 and 2.0 too.

For example, the GetHashCode() hash of the string “password” from .NET 3.5 is -733234769, while the hash from that exact same string in .NET 4.0 is -231203086. Scary, huh?

In light of that, we switched to using the SHA512Managed class to generate our hashes. Switching our code over wasn’t an issue (DRY for the win!), but having to email our customers to enter new passwords and security questions, which we also hashed the same way, wasn’t exactly fun. Not knowing their passwords apparently does have a downside! Here’s how we’re generating our hash codes now:

1
2
3
4
5
6
7
8
9
private const string _passwordSalt = "some_long_random_string";
 
public static string CalculateSaltedHash(string text)
{
	var inputBytes = Encoding.UTF8.GetBytes(text + _passwordSalt);
	var hash = new SHA512Managed().ComputeHash(inputBytes);
 
	return Convert.ToBase64String(hash);
}

Yay? Nay?

Nov 12

In the first post I gave a quick overview of what our deployment script does and why you’d want one. This post will go over some of the pre-deployment steps we take. Most all of this code will probably be pretty self explanatory, but I know just having something to work off of is a huge boost to starting your own, so here ya go.

1
2
3
4
5
6
7
8
9
10
11
function modify_web_config_for_production($webConfig)
{
	echo "Modifying $webConfig for production deployment."
 
	$xml = [xml](Get-Content $webConfig)
	$root = $xml.get_DocumentElement();
 
	$root."system.web".compilation.debug = "false"
 
	$xml.Save($webConfig)
}

Given the path to a web.config file, this function switches off the debug flag (and any other changes you’d need). Being a dynamic language, you can access XML keys quite easily. You’ll need the quotes around system.web since there’s the dot in the name though. Also, if you need access to any of the app.settings keys, you can use something like: $xml.selectSingleNode('//appSettings/add[@key="WhateverYourKeyIs"]‘).value = “false”.

1
2
3
4
5
6
7
8
function precompile_site($siteToPreCompile, $compiledSite)
{
	echo "Precompiling $siteToPreCompile."
 
	$virtual_directory = "/"
 
	exec { & $tools_aspnetCompiler -nologo -errorstack -fixednames -d -u -v $virtual_directory -p "$siteToPreCompile" $compiledSite }
}

This little beauty precompiles the site (located in the $siteToPreCompile directory, with the results output to the $compiledSite directory) using the ASP.NET compiler. I prefer to copy the actual compiler executable into the project folder even though it’s installed with the Framework. Not sure why. Anyway, $tools_aspnetCompiler can either point locally, or to C:\Windows\Microsoft.NET\Framework\vwhatever\aspnet_compiler.exe. You can also configure the options being passed into the compiler to suit your needs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function execute_with_secure_share($share, [scriptblock]$command)
{
	try
	{
		echo "Mapping share $share"
		exec { & net use $share /user:$security_full_user $security_password }
 
		& $command
	}
	finally
	{
		echo "Unmapping share $share"
		exec { & net use $share /delete }
	}
}

This is more of a helper method that executes a given script block (think of it as an Action or anonymous code block in C#) while the given share is mapped with some known username and password. This is used to copy out the site, create backups, etc. I’ll leave the $security_full_user & $security_password variable declarations out, if you don’t mind! We just put them in plain text in the build script (I know, *gasp!*).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
properties {
	$share_web = "wwwroot"
	$servers_production = @("server1", "server2")
	$live_backup_share = "\\server\LiveSiteBackups"
 
	$number_of_live_backups_to_keep = 10
}
 
function archive_current_live_site
{
	$current_datetime = Get-Date -Format MM_dd_yyyy-hh_mm_tt
	$one_of_the_production_servers = $servers_production[0]
 
	$web_share_path = "\\$one_of_the_production_servers\$share_web"
 
	echo "Archiving $web_share_path"
 
	$full_backup_path = "$web_share_path\*"
	$full_archive_file = "$live_backup_share\$current_datetime.zip"
 
	execute_with_secure_share $web_share_path {
		execute_with_secure_share $live_backup_share {
			exec { & $tools_7zip a $full_archive_file $full_backup_path } 
		}
	}
}
 
function delete_extra_live_site_backups
{
	execute_with_secure_share $live_backup_share {
		$current_backups = Get-ChildItem $live_backup_share -Filter "*.zip" | sort -Property LastWriteTime
		$current_backups_count = $current_backups.Count
 
		echo "Found $current_backups_count live backups out there, and we're aiming to keep only $number_of_live_backups_to_keep."
 
		$number_of_backups_to_kill = ($current_backups_count - $number_of_live_backups_to_keep);
 
		for ($i = 0; $i -lt $number_of_backups_to_kill; $i++)
		{
			$file_to_delete = $current_backups[$i]
			$extra_backup = "$live_backup_share\$file_to_delete"
 
			echo "Removing old backup file: $extra_backup"
			delete_file $extra_backup
		}
	}
}

These pair of methods create a backup of the current live site and make sure we’re only keeping a set number of those backups from previous runs, to keep storage and maintenance in check. Nothing too complicated. To create the backup, we just farm out to 7-Zip to compress the directory, which is ran withing nested execute_with_secure_share calls from above, which map the web server file share and backup file share. Likewise, the second method just gets a count of zip files in the storage directory and deletes the oldest ones in there until the total count gets to a specified count.

Conclusion

That’s the basics for what we do pre-deployment. Again, not really that complicated, but it can give you a starting point for your script. I’ll go over our actual deployment steps in the next post, then follow that up with some post-deployment goodness. I know, you can’t wait.

Sep 24

Pushing to production with a script? Crazy talk, right? Well, maybe not. Sure, there are lots of insane corporate setups out there where a script might not completely work, but for the vast majority of companies out there, this is totally within the realm of possibility. Maybe you want to save some steps when you’re deploying, or maybe you want to stop forgetting some of those crazy “best practices” people always talk about (building in release mode? turning off debugging in the web.config? pre-compiling sites?). Whatever the reason, a deployment script is a great solution.

What it does

Our current deployment script will:

  1. Get a completely clean copy of the code base from source control
  2. Build it in release mode
  3. Run all unit tests, slow/integration tests, and UI tests
  4. Switch off debugging in the web.config
  5. Pre-compile the site with the ASP.NET compiler
  6. Archive the current live site to a backup server, just in case (keeping a few previous versions as well)
  7. Deploy the latest copy of our 3rd party tools to each server
  8. XCopy deploy the site to each server in our cluster (taking down IIS first and letting our load balancer get users off that server)
  9. Visit the site with a browser to do all the first time pre-load reflection stuff (NHibernate, AutoMapper, StructureMap, etc)
    1. It’ll actually change its local DNS hosts file to make sure its looking at each server in the cluster too, so that each one is “primed”
  10. Make sure our error emails are working by visiting a test URL that throws an exception (therefore creating an error email), then logging into a special email account and making sure that email was received

OK, so this script takes a while to run (with all the tests taking up a majority of the time), but we gain a lot. A single click in TeamCity kicks the whole thing off, and we’re guaranteed little to nothing is broken in the system thanks to all the tests (unit, integration, and UI), that there’s backup copies if something does happen, and that everything is compiled/configured for production so we’re not missing any easy performance gains. I’d say that’s a win.

How it’s ran

We don’t have have this running in any automatic fashion, but instead run the target by hand from our build server whenever we’re ready. Our build server lets us easily schedule the “build” whenever we need to, though, so we can schedule it late at night so we don’t disrupt access. Our test servers are also being setup right now, so we’ll probably continuously deploy to those when they’re ready (twice a day? every check-in?).

Fail safes

There honestly aren’t a whole lot. As we come across certain failures we’ll add checks to keep them from cropping back up, but I didn’t want to complicate the script with all sorts of edge case checking if it’ll never need to worry about them. You need to apply the KISS and YAGNI principals to your build scripts just like your code. We do a few operations in try/catch blocks to make sure servers are kept down if they’re not deployed to correctly, or our 3rd party tools get cleaned up properly, etc., but not many.

I’m sure that’ll unsettle many of you, but a script like this is going to be highly customized to your environment, and your environment might have a higher chance of certain parts of the script failing, so you’ll need to guard against that. I’d highly recommend starting simple and only building out as situations arise though.

Build server bonuses

We use TeamCity as our build server, so I can’t speak about the others (CC.NET, Hudson, etc) and how much or little of the following benefits they offer.

The two biggest benefits we get, virtually for free, with using TeamCity to run our deployment script include:

  • Auditing - you can see who’s ran the script, and when
  • Tracking - it’ll show you, for each run, which changes were included in that deployment down to a diff of each file and source control comments
    • It’ll also show which changes are pending for the next run: Pending Changes in TeamCity
    • We don’t use a bug tracker that’s supported by TeamCity, but theoretically it can link right to each bug fix that’s included in each deployment

What’s next?

I’m going to show off parts of our build script and how we handle different situations in the next blog post(s). I’m not sure how many it’ll take or how deep I’ll go since much of it is situation specific, but I’ll definitely get you started on the road with your own.

As a heads up, this will all be written in PowerShell. We’ve moved our build script setup to it and it’s what made this deployment scenario possible.

Conclusion

Manual deployment sucks. Automated deployment rocks.

If there’s any way you can script your deployment (or even parts of it), I’d recommend it in a heart beat. It’s a bit bumpy getting it up and running, I won’t lie, but it’s a huge help once it’s stable and working. I’ll take a look at some of the basic pre-deployment steps we take in the next post.

Sep 2
Comments are just missed refactorings
icon1 Darrell Mozingo | icon2 Musings | icon4 September 2nd, 2010| icon32 Comments »

Ah comments, those delectable little nuggets of static information that routinely get out of sync with your code’s intention. Actually, I’m sort of excited when I run into them though. See, to me they represent missed refactorings that are usually pretty easy to implement.

Say you run across a piece of code like this:

1
2
3
4
5
6
7
8
9
10
11
12
public void ProcessOrder(Order order)
{
	// If the order is on hold, figure out why and get the next availability date.
	if (order.Status == OrderStatus.OnHold && order.OrderDate < DateTime.Now)
	{
		// <imagine complex logic to check reason here...>
		order.OnHoldReason = (result of complex logic above);
 
		// <imagine complex logic to get the next available date here...>
		order.NextAvailableDate = (result of complex logic above);
	}
}

So it’s usually not this obvious in real code bases, but almost every time I’ve run into a grouping of comments in this arrangement it could be boiled down into something this simple. Can you see the refactoring potential? It’s pretty easy - extracting some variables and methods based upon the text in the comment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void ProcessOrder(Order order)
{
	var orderIsOnHold = (order.Status == OrderStatus.OnHold && order.OrderDate < DateTime.Now);
 
	if (orderIsOnHold)
	{
		order.OnHoldReason = getOnHoldReason(order)
		order.NextAvailableDate = getNextAvailableDate(order);
	}
}
 
public string getOnHoldReason(Order order)
{
	return // <imagine complex logic to check reason here...>
}
 
public DateTime getNextAvailableDate(Order order)
{
	return // <imagine complex logic to get the next available date here...>
}

Like I said, it’s not much and it’s certainly not hard (a few keystrokes in Visual Studio, with or without ReSharper), but it moves the comments from sitting idly above the code, not participating, to being true first class citizens in the program in the form of variable and method names. It doesn’t guarantee they’ll be updated with the code if the logic changes in the future, but it gives them a lot better shot at it. I mean, there aren’t really developers out there lazy enough to update the intention of a variable or method and not change the name, right? Right?

Aug 26
Controlling IIS7 remotely with PowerShell
icon1 Darrell Mozingo | icon2 Misc. | icon4 August 26th, 2010| icon3No Comments »

Our deployment script needed to do some basic IIS administrative tasks remotely on a Windows 2008 (non-R2) server, which runs IIS7, recently. Finding the information and fiddling around with it took me a good day and a half, so I thought I’d post the steps here to help someone else (more than likely myself) in the future:

  1. Download the Windows Management Framework Core package for your setup
  2. If your machine is something older than Windows 7 or Server 2008 R2, you’ll need to get the PowerShell IIS7 Snap-In
  3. If your workstation/build server and target web servers happen to be on different Windows domains, you’ll need to run this one time on each client machine:
    1
    
    Set-Item WSMan:\localhost\Client\TrustedHosts *
  4. Run this command once on each server:
    1
    
    winrm quickconfig
  5. You’ll need to load the PowerShell Snap-In once on each client, which differs depending on which version of Windows you’re running. Anything older than Windows 7 or Server 2008 R2:
    1
    
    Add-PSSnapin WebAdministration
    Windows 7 and Server 2008 R2 run:
    1
    
    Load-Module WebAdministration
  6. Check if it’s working properly by running this command on any version of Windows (you should see the IIS7 Snap-In listed):
    1
    
    Get-Module -ListAvailable
  7. Get credentials for accessing the remote server
  8. Start running some remote commands on your web servers:
    1
    
    Invoke-Command -ComputerName "webserver_computerName" -Credential $credentials_from_last_step -ScriptBlock { Add-PSSnapin WebAdministration; Stop-Website IIS_Site_Name }

It’s not really that hard once you get the proper packages installed and the permissions worked out, and since it’s so powerful and useful for scripting purposes it’s well worth the trouble. The available commands are awesome for use in automated deployment scripts.

You can learn more about the PowerShell Snap-In provider here, and at its download site here.

Aug 19
Clever vs Explicit
icon1 Darrell Mozingo | icon2 Musings | icon4 August 19th, 2010| icon31 Comment »

When we all start out developing, either through classes in high school/college or slowly on our own time, we inevitably want to write code thats super clever. Like, use-3-lines-to-express-what-used-to-take-20-lines type of clever. We see less code and we’re pleased. All is good and well with the world.

Until you look at that code a few months down the road and wounder what the hell you were smoking, and it takes you almost as long to decipher it again as it did to write it in the first place. All of a sudden saving those few extra lines of code don’t seem so smart, huh?

The simple fact of the matter is you spend more time maintaining code than you do writing it in the first place, so being more explicit in your code always trumps being clever to save a few lines. Always.

There’s the obvious places where people get clever, like algorithms or loops, but there’s plenty of other places too. Places where I wouldn’t really call it “being clever”, or at least I’m sure the original authors never thought they were trying to be clever when they wrote it. It was probably just quicker to write it in a certain way. For example, take this code:

1
2
3
4
5
6
7
8
var mappedEmployees = new List<EmployeeViewModel>();
 
foreach (var employee in _employeeRepository.All().Where(x => x.IsRetired == false && x.Salary > 100000))
{
	mappedEmployees.Add(_employeeMapper.Map(employee));
}
 
return View(mappedEmployees);

It’s not really hard to read, but it’s not really easy either. It might take you an extra second or two to figure out what’s going on when you first look at (even if you wrote it a few months ago), but multiply that by how many places you see code like this and how often you go back in to modify it (for new features, bugs, whatever). It adds up, quick. Written more explicitly, it might look something like this:

1
2
3
4
5
6
7
8
9
10
var mappedEmployees = new List<EmployeeViewModel>();
var nonRetiredHighEarningEmployees = _employeeRepository.All().Where(x => x.IsRetired == false && x.Salary > 100000);
 
foreach (var nonRetiredHighEarningEmployee in nonRetiredHighEarningEmployees)
{
	var mappedEmployee = _employeeMapper.Map(nonRetiredHighEarningEmployee);
	mappedEmployees.Add(mappedEmployee);
}
 
return View(mappedEmployees);

You might call it verbose, but I’d say it’s a net gain. Each line is doing one thing. Yon can step through and read it without mentally pulling pieces apart. None of this “OK, that’s the mapped object call there, and its return is going into the collection there, and the whole thing is looping through that query there”. Things are given names and methods aren’t nested inside each other.

Always be on the lookout for “clever” areas in your code. Be explicit. Try to stick to each line doing one thing so there’s no hidden surprises.

« Previous Entries Next Entries »