Sunday, December 26, 2010

Continous Installation : going beyond the I of installation

The installation of software towards our customers goes very smooth :-) I've set this system up about 2 years ago, and it is a very appreciated part in our company. Hundreds of installations done.
From the start on I extended the functionality beyond the basic installation part, from day 1 actually, but I never wrote about it yet. I also collected some data of those servers : firstly just the application event log. The idea was to send us the error logs, so we were informed if a customer called. And we can do statistics on these errors. What application throws the most errors, are these technical, validation, communication, ... related and so on. Very usefull information.
Later on, I extended it even more to watch the server itself, CPU usage, hard disk usage and even SQL Server information : backup ok, size of database, size of tables, ...
All this info is send to our central SQL server for analysis in the same way as the rest : via the FTP site. This also proved very usefull since we discovered hard disk failures and bad memory modules before they could do serious damage !

But now a new challenge appeared : Hyper-V

Our next servers are more powerfull and we want to reduce the amount of time we need when we have to replace one. The answer is Hyper-V.
These servers are loaded with Hyper-V Core, and above that we will host 1 or more virtual servers. If the hardware needs updating, we can just put in a bigger server with Hyper-V Core on it, move the virtual servers on to it, and we're done. All settings are kept as the were before the move : Big Advantage !!

The catch : we also need to watch the Hyper-V machine itself as we do the current servers. System team looked around for Hyper-V remote stuff, but could not find anything (payable). When I heard of this, I said :
Just use CCNet :-D We use it on all of our other servers and it works, so why not on a Hyper-V one?

And it worked indeed, Hyper-V Core comes with .Net framework 2.O loaded, and that's all I need. To be on the safe side I use the same (very old) version of CCNet as we do on the other servers : 1.4.2 (yikes indeed very old).
But we only use the exec task and the basic triggers, so it is all we need.
just copy the CCNet setup exe on it via a share, run it through a command prompt, and CCNet is installed. Could not be easier !
Now we can monitor and maintain the Hyper-V servers the same as the other ones.

Thursday, August 5, 2010

Continous Installation : Forcing multiple builds at once

It's been about 2 years since I've set up CCNet for Continuous Installation at work, and it works like a charm. Programs we install with CCNet are at time of writing :Off course some programs also require a database, so the database updating is also done as a part of the installation :-).

But now another need has surfaced : Program Dependencies.
We make software for the local government (City hall level), but this is all kinds of software : finance, registry of births, deaths and marriages, construction requests and so on. What we do NOT want is that multiple programs need to handle the state of a person (married, divorced, ...). There is 1 program that handles this kind of stuff, and others can interface with it. For the moment these interfaces are done via WCF services. So there is no 'real' reference like with a function library, but more like with an interface library. And so far this works great.

For example when a new version of lets say Bookkeeping is needed, I may also need to install or update PeopleManagement, because of the new dependency between Bookkeeping and PeopleManagement. Now as long as it is only 1 dependency, I could just schedule them after each other, but there are programs with 5 or more dependencies :-(
And doing it by hand also means that I (or a collegue) may forget to update a dependency, which is not good.

There is the ForceBuild Publisher, but this has a big problem, it just launches the projects with the Fire and Forget strategy. Meaning that I do not have control of the exact order of execution, I have no control what to do when a dependency fails, ...

Luckily the code of that publisher is a good starting point, the only things I need to add is to wait for the outcome, before returning control to CCNet-core and foresee a property to fail the task if the forced build failed.

The current code of the ForceBuild is as follows :
public void Run(IIntegrationResult result)
{
if (IntegrationStatus != result.Status) return;
factory.GetCruiseManager(ServerUri).ForceBuild(Project, BuildForcerName);
}
With the adjustments, the code is :
public void Run(IIntegrationResult result)
{
//get cruisemanager
var cm = factory.GetCruiseManager(ServerUri);
var cpi = GetCurrentProjectInfo(cm);

//get last known build date of project to be forced
var lastBuildDate = cpi.LastBuildDate;
var compareBuildDate = lastBuildDate;

//force build of wanted project
cm.ForceBuild(ProjectName, buildForcerName);

//keep waiting until build is done
while (lastBuildDate.Equals(compareBuildDate))
{
System.Threading.Thread.Sleep(500);
cpi = GetCurrentProjectInfo(cm);
compareBuildDate = cpi.LastBuildDate;
}

if (FailIfForceBuildFaled && cpi.BuildStatus != IntegrationStatus.Success)
{
throw new CruiseControlException("Force Build Failed of : " + ProjectName);
}
}

private ProjectStatus GetCurrentProjectInfo(ICruiseManager cm)
{
var previousBuildInfos = cm.GetCruiseServerSnapshot();
ProjectStatus result = null;

foreach (ProjectStatus ps in previousBuildInfos.ProjectStatuses)
{
if (ps.Name == ProjectName)
{
result = ps;
continue;
}
}

return result;
}


Not much change in code, but a very great increase in the ease of installing our software. Now I just schedule this project, iso many others :-)

Monday, July 19, 2010

back in action

It's been a very long time since I made a post, due to various reasons. My work got all my time due to a project that went bad :-( This is now solved, so meaning more time for CCNet now : Yeah !
I do need to catch up, so give me some time. There is a second release planned of the 1.5 version which should fix some annoying bugs. ETA should be in 1 to 2 weeks. As for the 1.6 release, this will probably be around October or so, depending on the amount of new items.

I did make a small new addition to the 1.6 code base: a Cron Trigger.
For the people working with a Unix system, this sounds familiar, for those not knowing, it is a time-based job scheduler. Cron enables users to schedule jobs (commands or shell scripts) to run periodically at certain times or dates. It is commonly used to automate system maintenance or administration.

Because the Cron syntax is very simple it is also very powerfull. Scheduling something every tuesday every 10 minutes between 15 and 16 hours, but only in the weekends is very easy.
You can see the documentation at Cron Trigger
All the hard work was already done, there is a project NCrontab so it was just adding the code, very easy.

Next items will be crawling through Jira, and fixing open issues, so that will keep me busy for some time.

Tuesday, April 13, 2010

Continuous Installation and MS SQL Reporting Services

A few weeks ago, I was informed that we also will use MS SQL Reporting Services in our programs for the reporting. Now I was used to Crystal Reports, which are easy to deploy because they are compiled into the assembly. But fust for making things more complicated, the company decided to use MS SQL Reporting Services.
From deployment point of view, this is a pain !
The reason : there is no real deployment strategy for seen by MS (as far as I could find out). There are 3 options :
° distribute via the publish in Visual Studio
° use the SOAP interface exposed by MS SQL Reporting Services
° there is an rs.exe, but the usage is just medieval.

Option 1
NO GO, not for 75+ servers !
this requires direct access to all of the servers, can not be sheduled, would be difficult with the current version setup we have, ....

Option 2
This would mean writing a program for communicating with the soap interface, plus its errorhandling, and more important : the data (reports and the like) must still be supplied somehow

Option 3
This seemed the best way to start, and it turned out to be so. The rs.exe is indeed medieval, but there is a nice UI : RSScripter.
RSSCripter makes a batch file that calls rs.exe with all the needed arguments (neat), and exports all wanted items into a folder structure.


So I went for option 3, the approach is as follows :
1) our reporting guy creates the various reports (yes we have a real reporting guru)
2) when he thinks if all are some reports are ok, he uses the RSScripter to export all needed items (reports, datasources, folders, ...)
3) he places this in a dedicated folder in source control, each 'version' is a subfolder of this dedicated folder.
4) he presses 'force build' on the 'make package' project, which just zips the folder

and now I have a package I can deploy :-)

For the deployment itself, there are still some small steps needed though :
° adjust the batch file
a) update the log file location
b) update the scriptlocation
c) update the target reportserver
° adjust some rss files (report and datasource files)
a) update any connection strings
b) update user and password
c) update the source location


when all this is done, I just call the updated batch file.

Nice and easy

Sunday, January 31, 2010

WPF : Using the designer with design time data

Back on the WPF track. Since it is really needed at my company, I re-started (again). But I finally have some good news :-). Before, I wrote all the WPF programs with 'NotePad'. Not really NotePad, I did use VS2008, but without the designer.
The designer does not work with the programs at work! and I thought : yup another glitch in WPF....

So I started wondering why this would be, and started from scratch.
° add a screen : designer works
° add a listbox : designer still works
° bind the listbox itemsource to values (via a dataprovider) : designer works
° retrieve the data from a database via wcf service via company framework: designer does NOT work ????

Meaning it had to be something with the WCF service or the company framework, but since the program does work in runtime, this would be hard to pinpoint(maybe due to the security - login). So I tried another approach : The basic idea is that I want to make a screen, and preferably with some dummy data so I can use the designer. This data does not have to be data from the database.

Conclusion : in design time the dataprovider may retrieve dummy data, in runtime it has to go via the wcf service to the database.
And there may not be any code changes required after the design is done. Otherwise there is a risk of showing dummy data in a production environment!

Solution : different dataproviders.
In the old way of working I had 1 dataprovider class, mostly called DataProvider
Now I split this up into 3 classes and 1 interface :
° DataProvider : the one the xaml file(s) bind to like before.
° DataProviderDesignTime : this one provides dummy data
° DataProviderRunTime : this one goes via the wcf service to the database
° IDataProvider : holds all the methods of the various dataproviders

Way of working :
° Dataprovider has a private variable of type IDataprovider, initialised to DataProviderDesignTime.
° At runtime, one of my first commands is to change this IDataprovider in the Dataprovider class from design-time to run-time, via a shared function called SetRuntimeDataprovider

Now this saves me a lot of time : I can finally use the designer !!
And even the effect of convertes can be shown now, cool.

The code

<Window x:Class="WpfApplication8.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lc="clr-namespace:WpfApplication8"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="ListAllPersons"
ObjectType="{x:Type lc:DataProvider}"
MethodName="ListAllPersons"
/>

<DataTemplate x:Key="PersonTemplate" >
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}" />
<Label Content="{Binding Age}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding Source={StaticResource ListAllPersons}}"
ItemTemplate="{StaticResource PersonTemplate}" >
</ListBox>
</StackPanel>
</Window>



using System;
using System.Collections.Generic;

namespace WpfApplication8
{
public class DataProvider
{
private static IDataprovider dp = new DataProviderDesignTime();

public static List<Person> ListAllPersons()
{
return dp.ListAllPersons();
}

public static void SetRuntimeDataprovider()
{
dp = new DataProviderRunTime();
}
}
}

using System;
using System.Collections.Generic;

namespace WpfApplication8
{
interface IDataprovider
{
List<Person> ListAllPersons();
}
}

using System;
using System.Collections.Generic;

namespace WpfApplication8
{
class DataProviderDesignTime : IDataprovider
{
public List<Person> ListAllPersons()
{
List<Person> result = new List<Person>();
for (int i = 1; i < 10; i++)
{
result.Add(new Person { Name = "Clone " + i.ToString(), Age = i });
}
return result;
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WpfApplication8
{
class DataProviderRunTime : IDataprovider
{
public List<Person> ListAllPersons()
{
// get the data
}
}
}