Archive

Posts Tagged ‘CCTray’

The Missing Piece: Encrypted Communications

Introduction

In my last post (read it here) I wrote about an issue with security – that the actual communications between the client and the server were not secure. I also outlined the approach I was taking to remedy the issue.

Before I move onto my solution, I must mention that this is not a perfect solution. As one of my readers commented, this does not really cover a man-in-the-middle attack. It will prevent network sniffing and replay attacks, it is still possible to insert a listener between the client and the server.

It is possible to configure .NET Remoting to intrinsically secure communications – in which case there is no need to worry about this from the client side at all. However, it is not easy easy as it might appear, so I decided to finish my partial solution.

In time, I hope to be able to look at expanding the communications channels. Currently the only channel for communicating with the server is .NET Remoting – which was because of the way methods used to be called. Now with the new messaging-based framework, it is easier to add new channels. Hopefully I will get some time to look at using SslStream and NegotiateStream to secure communications.

Client Communications

First, how does the new code send encrypted messages to the server? This involved expanding how messages are currently sent.

Currently messaging uses the following model:

Communications

Using the new communications model, each client application will generate an instance of a CruiseServerClient. Actually they generate a CruiseServerClientBase instance, but the class that implements this abstract class for the new framework is CruiseServerClient. This class does not actually send any messages – instead it translates the method calls (e.g. ForceBuild) into messages (e.g. ProjectRequest), sends the message and then processes the response.

The actual sending is done by an instance of IServerConnection. This instance takes the specified messages and pushes them over the specified channel (either .NET Remoting or HTTP). Because the actual channel is encapsulated by an IServerConnection, it is easy to replace the channel with a different one.

For encrypted communications I added a new channel called EncryptingConnection. This channel is then inserted between the client and the underlying communications channel:

EncryptedCommunications

This means that the messages are encrypted (both ways), but the underlying transports (e.g. HTTP or .NET Remoting) can still be used without any changes.

There are a couple of extra changes. First, the original request message gets replaced by an EncryptedRequest message. This contains the original message, but encrypted (of course), and the original action that was requested. Secondly, the action has been changed to ProcessSecureRequest – more on this when I cover the server side changes.

EncryptingConnection is also responsible for the hand-shaking required to set up the encrypted communications. When a message is sent, the connection checks to see if it has a password. If it does not have a password, sets up a new common password by the following process:

  1. Ask the server for its public key
  2. Generate a new secret (using RijndaelManaged)
  3. Encrypt the secret using the server’s public key (using RSACryptoServiceProvider)
  4. Send the secret to the server and wait for the response
  5. Store the secret locally

Once this has been done, all messages are then encrypted using the common secret (again with RijndaelManaged). This includes both request and response messages.

The actual sending of the messages is then done by passing the message onto the underlying channel.

Server Communications

Now that the client can send messages, we need to look at the server side. After all, if the server can’t understand the messages, then nothing will be done!

All communications into the server come in via CruiseServerClient. This is a MarshalByRefObject-derived class that uses .NET Remoting. All HTTP communications actually go via the dashboard, which passes them onto the server using .NET Remoting. So this model simplifies things – I only need to change CruiseServerClient!

Currently CruiseServerClient has one overloaded method for processing messages – ProcessRequest(). This takes the incoming message, works out which method to call, converts the message into the required objects (if required) and then calls the method. It also handles generating the response and adding any error information.

To handle secure messages I have added a ProcessSecureRequest (). This method is called by ProcessRequest() and acts as a middle-man. Basically, when ProcessRequest() gets a secure message, it passes it onto ProcessSecureRequest(), which then works out which methods to call, converts the message, etc.

I have also added three other new methods:

  • RetrievePublicKey() – this will send the server’s public key to the client. If the server does not have a public/private key pair it will generate the pair (using RSACryptoServiceProvider).
  • InitialiseSecureConnection() – this receives the common secret from the client, decrypts it and stores it locally.
  • TerminateSecureConnection() – this will remove the common secret for a client from the local store.

These methods are called as part of the hand-shaking between the client and the server.

Additionally, as part of these changes, I have added a new property to the messages called ChannelInformation. This property has information on how the message was transported – i.e. metadata about the message itself.

Currently, there is only one implementation – RemotingChannelSecurityInformation. This says the messages was transported using .NET Remoting. On this class is a property called IsEncrypted. ProcessRequest() generates an instance of this class and sets IsEncrypted to false, while ProcessSecureRequest() generates an instance with IsEncrypted set to true.

Turning On Encryption

Encryption is turned on from the client-side. When a client wants to communicate with a server using encrypted communications, they need to set UseEncryption to true in ClientStartUpSettings.

Currently, this is not possible with the dashboard, but I have added it to CCTray. When a new server connection is added (for either .NET Remoting or via the dashboard), there is a new option of turning on encryption:

CCTray Encryption

To use encrypted communications, all that is needed is to check this box.

Unfortunately, due to the way CCTray is configured, it is not possible to turn on encryption for an existing server. Instead the server needs to be removed and re-added (yuck!) This is probably a good area for tidying up in a future release of CCTray.

Enforcing Encryption

All this work so far has been to set up encrypted communications. At this point, it is up to the client to decide whether the channel is encrypted or not – the server doesn’t really care. We need some way of telling the server to only accept encrypted communications (i.e. to enforce encryption).

To do this, I have added a new configuration option to the server security settings – channel. This is an optional setting and has one required property – the type of channel. The following shows how to configure a channel to use encrypted messages:

  1: <internalSecurity>
  2:   <channel type="encryptedChannel"/>
  3:   <users>
  4:     <!-- Omitted for brevity-->
  5:   </users>
  6:   <permissions>
  7:     <!-- Omitted for brevity-->
  8:   </permissions>
  9: </internalSecurity>

This tells the security manager it will only accept communications over an encrypted channel.

When a message is received by the message processer (i.e. after it has been received by CruiseServerClient, processed and passed on), it calls a method called ValidateRequest(). This does some simple validation checks (the message is less than a day old and it hasn’t been duplicated). I have expanded this validation check to detect a channel definition and if present to validate it.

The Basics Of Encryption

This provides some of the basic pieces for encrypted communications. It certainly is not a comprehensive solution – there are still some areas uncovered (like validating the server is who it says it is).

In terms of where this can go, there are the following items that can be included:

  • Additional encryption mechanisms (e.g. DES, triple DES, AES, etc.)
  • Additional security channels (e.g. SSL, negotiated)
  • Validation of the server (perhaps via X.509 certificates)
  • Use encryption from the dashboard

But this provides a start. Let me know what you think so far.

CCTray Backups

Sometimes when I have some spare time at work I add some minor improvements to CruiseControl.NET. There is only one problem – when I’m adding improvements I don’t want CCTray to point to our actual CruiseControl.NET instance.

What I normally do is open up the settings, remove all my current projects and then add the test projects. When I’ve finished I then need to go through the same process again to add my work projects! As you can guess this is not the best approach :-(

So, I’ve added a new tab to the settings in CCTray:

Backup

As you can see, it’s nothing flash. But it does allow me to save my current settings and then reload them.

The actual code is pretty simple, it just opens a dialog (either load or save depending on the action) and waits for user input. When the user enters a valid file name it uses CCTrayMultiConfiguration to perform the actual work. under the hood this class uses XML serialisation to perform the actual saving, so it’s pretty easy to use.

The only gotta that I came across was in loading the settings. I also need to reload the settings in the configuration window – otherwise it continued to display the old settings. But it wasn’t too bad – other than a couple of quirks in the way the display is loaded.

So, hopefully this will make things easier for other people as well.

Categories: CruiseControl.Net Tags:

It’s Been a While

A Quick Update

I haven’t written a post for while now – not because I haven’t done anything, but because I’ve been focusing on getting a lot of my work into the trunk (that and my son is taking a lot of my time).

So here is a quick update on what has finally made it into the trunk.

1.4.4 Release

Some of my work was able to go into the 1.4.4 release. The following items have been included:

  • Server extension points
  • Simplified web administration
  • Installation packages
  • Package publisher
  • NDepend task
  • Extended source control exception handling

These changes have been added successfully and appear to be stable (let me know if there are any issues). Additionally I’ve also done a little bit of work on making the validator more useful (extended validation, its own installer, etc.)

These will all go out in the next release (hopefully soon!)

1.5.0 Release

This is the exciting one as security has finally made it! We did take a little step back and decided not to add either the messaging or communications client code as yet. Some of the security relies on this code, so it hasn’t been implemented yet (still pending a decision on whether we add this in the 1.5.0 release or wait for a future release).

Additionally I have added the build throttling extension and the task status break-down (just finished tonight).

I’m going to add the dynamic build parameters over the next week or so and then that will be most of my work finally committed to the trunk (with the main exception of messaging/communications client.) After this it will be a time of settling down and resolving any issues that come up (especially with security).

Visual Studio 2008

The other great news is we finally converted to Visual Studio 2008 for the solution and project files. This has both made my life easier (hence the new additions) and also allowed us to start using C# 3.0.

While we have not moved to .NET 3.5 yet, this is still a great step forward!

So a lot of work is happening behind the scenes and we are looking forward to a new release soon!

Communications Client and Extensions

A Long Story Short

When I first started development for CruiseControl.Net, I wanted to add a WCF transport mechanism to the product. Now I did achieve this, but it took a fair amount of work. The way I original did it was to provide implementations of the transport classes within CCTray, plus I added a server extension to handle the server-side.

This all involved a lot of work, as I needed to implement all the important methods in the transport classes, plus in the end, this only worked between CCTray and the server – the dashboard was excluded from the process.

Since then, I’ve come a long way, both in my understanding of the code, plus with the modifications I have done. Going back and looking at this, I now realise that there is a much easier way to do this! Interested? Read on…

The Communications Client

Recently I have modified the code of CruiseControl.Net to use a messaging-based structure for the method calls between the client and the server. This involved a modification to ICruiseManager (which I later renamed to ICruiseManager2), and then a whole new set of communications classes. These classes all reside in the Remote project (plus they are also referenced by the Communications project).

When I started to implement the client, I realised that there were lots of methods that I would need to implement, and I’d need to implement the communications for all of them. Rather than doing this, I decided on an alternate approach.

I added a new interface called IMessageProcesser, which contains a method called ProcessMessage(). I then modified the server class to implement this interface. This interface has one purpose – it allows the client to send a message to invoke a method. This means remote clients now only need to implement one method – ProcessMessage().

The communications client is built on this base. The main communications client has all the standard methods on. However, it converts these methods into message objects and then passes them onto a connection instance. This connection instance passes on the message via ProcessMessage(). Now whenever a new method needs to be added, only one class needs to be modified – the communications client class.

To simplify the process of developing new communications channels, I added an interface called IServerConnection. This class handles all the communications between the client and the server. This maps to IMessageProcesser, but allows for both synchronous and asynchronous models (planning ahead for Silverlight).

The main method in this interface is SendMessage(). This converts the message into the however the transport protocol wants it and then send the message. It is also responsible for processing the response message.

As an example, this is how it has been implemented for the HTTP connection:

  1: public Response SendMessage(string action, ServerRequest request)
  2: {
  3:     // Generate the target URI
  4:     Uri targetAddress = new Uri(serverAddress,
  5:         string.Format("/server/{0}/RawXmlMessage.aspx", request.ServerName));
  6: 
  7:     // Build the request and send it
  8:     WebClient client = new WebClient();
  9:     NameValueCollection formData = new NameValueCollection();
 10:     formData.Add("action", action);
 11:     formData.Add("message", request.ToString());
 12:     string response = Encoding.UTF8.GetString(client.UploadValues(targetAddress, "POST", formData));
 13: 
 14:     // Convert the response into a response object
 15:     Response result = XmlConversionUtil.ProcessResponse(response);
 16:     return result;
 17: }

The initialises a WebClient, adds the form data (the message is serialised to XML using the ToString() method) and then sends it to the server. The response is then processed via a helper method (XmlConversionUtil is a public class that can be used by other protocols) and returned to the caller.

The Server Side

As well as the client, the server often needs to be modified for receiving messages. To handle the actual communications, a server-side extension will need to be added. I have already done this for the original WCF work, but now that things have changed, I need to return and review it.

A server extension must implement ICruiseServerExtension. This class provides a number of methods that the server will call (e.g. Start(), Stop(), Initialise(), etc.), plus it receives an instance of ICruiseServer. This instance contains events that an extension can listen to (e.g. ProjectStarting, ProjectStarted, ForceBuildreceived, IntegrationStarted, etc.)

There is only one issue with using extensions – CruiseServer does not implement IMessageProcesser, so an alternate way is needed to process incoming messages. The good news is this is very easy to do – just instantiate a new CruiseManager instance. CruiseManager does implement IMessageProcesser, and it is a wrapper around CruiseServer.

To show how easy this is, I quickly put together a stub of an in-coming message handler:

  1: using ThoughtWorks.CruiseControl.Remote;
  2: 
  3: namespace ThoughtWorks.CruiseControl.Core
  4: {
  5:     class MessageProcessingExtension
  6:         : ICruiseServerExtension
  7:     {
  8:         private IMessageProcessor processer;
  9: 
 10:         public void Initialise(ICruiseServer server, ExtensionConfiguration extensionConfig)
 11:         {
 12:             processer = new CruiseManager(server);
 13:             // TODO: Initialise in-coming message server
 14:         }
 15: 
 16:         public void Start()
 17:         {
 18:             // TODO: Start in-coming message server
 19:         }
 20: 
 21:         public void Stop()
 22:         {
 23:             // TODO: Stop in-coming message server
 24:         }
 25: 
 26:         public void Abort()
 27:         {
 28:             // TODO: Abort in-coming message server
 29:         }
 30: 
 31:         // TODO: Call this method when an incoming message is received
 32:         private string HandleIncomingMessage(string action, string message)
 33:         {
 34:             return processer.ProcessMessage(action, message);
 35:         }
 36:     }
 37: }

Conclusion

This shows how the new message-based infrastructure makes it simple and easy to add a new protocol to CruiseControl.Net.

Client-side changes are done by implementing IServerConnection, while server-side changes can be implemented via ICruiseServerExtension. There are a number of helper classes that simplify the process, including XmlConversionUtil on the client and CruiseManager on the server.

There is still a bit of work to be completed still, need to modify both CCTray and WebDashboard to use the new communications client. This is mainly grunt work, just taking the time to implement it, so it shouldn’t be too far in the future.

Playing with Packages

Still Downtime

We’re still waiting on the 1.4.3 release, so I thought I’d try and implement another of my ideas. At the moment the implementation isn’t perfect – I know it needs some fine-tuning – but it provides another interesting possibility for what we can do with CruiseControl.Net.

Packages

I spent three years working for a university here in New Zealand, so I’ve had some experience with content management in this field. One of the concepts that’s been around for a while is the concept of a package – a set of files that are related, plus a manifest describing these files (and how they can be used). These packages are normally stored as a ZIP file to allow ease of transport.

The reason for a package is to enable blocks of content to be passed around easily. You’re setting up a new course, and discover that someone else has already written a module on one of your topics. Rather than re-writing the whole module, you just import the content package into your Learning Management System.

As you can see packages simplify knowledge transfer and make it easier for people to use it. Now in CruiseControl.Net we’re not interested in learning content (or at least most of us aren’t), but we do have build artefacts – the results of a build process. It would be nice to have some simple and easy way to transfer these around.

Packages for CruiseControl.Net

I thought I’d implement packages for CruiseControl.Net. A package can be defined as a group of files in a ZIP archive, that may or may not include a package manifest. The manifest lists all the files, plus contains a header detailing the build that generated the manifest (this is an area that probably needs a bit more work).

Since I wanted to give people control over what is packaged, I have added a package publisher. This is an example of how a package is defined:

  1: <package name="Binaries" manifest="true">
  2:   <files>
  3:     <file>deploy\*.sql</file>
  4:     <file>deploy\*.msi</file>
  5:   </files>
  6: </package>

The key attribute is the name – this will be the name of the package when it is generated. I’ve made the package so the manifest is optional, and this example shows how to turn it on.

The other important part of the definition is the files element. This defines which files should be included in the package. In the example I’ve shown how a wild card can be used for the file list, additionally individual files can also be specified.

There are a few more options for the publisher. The full list is:

Name Type Description
name string (required) The name of the package.
compression integer (optional) The compression level to use. This must be a value from 0-9, where 9 is the highest compression.

The default value is 5.
always boolean (optional) This records whether a package should always be generated. If this is off, then a package is only generated for successful builds. If this is on, then a package is generated whether the build succeeds or not.

The default value is false.
flatten boolean (optional) Whether to flatten the file structure in the package or not. If this flag is off then the directory structure is also included, otherwise just the file names are included.

The default value is false.
single boolean (optional) Whether a package should be generated for each build, or just one package for the project.

If this flag is off, then a package file is generated for each build and stored in the artefacts folder under a new folder for the build. This is useful for maintaining a history of the packages, but can result in a large number of packages accumulating.

If this flag is on, then only one package will be generated. When the package is published, it overwrites the previous package.

The default value is false.

fromWorking boolean (optional) Sets whether to get the source files from the working folder (true) or the artefacts folder (false).

The default value is false.
manifest boolean (optional) Whether to include a manifest file (true) or not (false).

The default value is false.
files string array (required) The files to include.

I’ve tried to cover all the most obvious options, but like I said earlier, it still needs a bit of polish.

The following shows the package that is generated in this example:

Packages3

Since I didn’t set the “single” option, it will be created in its own folder underneath the artefact folder.

Viewing Packages

Generating the packages is only half the group – the other half is to make these packages easier available to the users of the system.

To achieve this there are two places these packages need to be: in the dashboard and in CCTray. As such I’ve made a couple of changes to ICruiseManager to allow transmitting files (I could have done it via a shared folder, but that always assumes folder will be available, which might not be the case over the Internet). I’ve also added a method to retrieve a list of files. I’ll go into the technical details in a future post, but here are some shots of how it works.

First, the package list is available from CCTray by right-clicking on a project:

Packages1

I have added a menu option called “Packages”, plus a short-cut key for it. Clicking on this option will bring up the list of packages:

Packages2

This contains some obvious data (name, date and size), plus the build that the package is from. Double-clicking a package, or clicking on the download button (the little globe with an arrow) will first prompt the user for a download location, and then download the package.

The dashboard also allows downloads of packages, although in a slightly different way. Instead the list of packages is included on the project status, and are downloaded by clicking on the link.

Under the Hood

As a final note, I’ve implemented this functionality using #ZipLib from the #Develop team. It works nicely and without any issues.

The transfer of files across .NET Remoting is a little, since passing a stream is not recommended. Instead a stream “rip-off”, that provides similar functionality. This means it’s up to the client to control how data comes across.

Finally, the storage of the files is just in the file system. The more I play around with CruiseControl.Net, the more I question it’s reliance on the file system for all storage. It would be nice to have a centralised data store that could be accessed by the different parts without going through either the file system or the server, but I guess this is a topic to look at in future.

Where’s the Code?

At the moment this code isn’t publicly available. The reason why is simple: I started playing around with this before the change freeze (although I didn’t get far), and as such it’s based on the trunk code. Now that the change freeze is in place, I can’t deploy it until the release :-(

So until the change freeze is lifted, this code will have to wait…

In meanwhile, I’ll look at writing a post that dissects how this functionality works. That way i won’t forget how it works either!

Playing Around

27 February, 2009 4 comments

It’s Down-time

Dave, the project owner for CruiseControl.Net has returned from his holiday and we are hoping to get the next release of CruiseControl.Net (1.4.3) out soon. To help with this we’ve decided to have a change freeze (we did some more fixing of issues while he was away) to ensure we have a stable code base for release.

I’ve also decided to pause on the messaging and communications client work I’ve done – otherwise things are starting to get too messy :-( As such I’ve got some down-time until the 1.4.3 release goes out (yes, i could work on merging the security changes, but I prefer it when I have a target to hit, rather than vague promises of soon).

So, I thought I’d take a quick look at an idea that’s been bouncing around in my head for a while.

The Current CCTray

I’m sure a lot of people have seen that their project is building, so they open up CCTray and get a view like the following:

Status-1

This tells us that the project is building – but not much else!

For example, what is the current task? Has anything failed in the build? Has the source been fetched yet? Etc, etc, etc.

The dashboard is a little bit better – at least it tells us what is running at the moment. But there’s no history of what’s happened (unless we look at the logs) – plus it’s still very high-level.

Instead, I’d like a display that shows everything that is in the project – plus what has happened, what is currently happening and what is due to happen. It would also be nice to get an estimate of how much longer, plus some details on the current item.

My Rough Prototype

So, I’ve put together a rough prototype of how this could work (and look). Here are some screenshots of how this would look in CCTray – bear in mind that this is at a very early stage, it still needs a lot of tidying up :-)

First off, here is a general view of the project status:

Status-2

This shows a snapshot of the last run of the project. In this case there were no changes, so most of the tasks were cancelled – as was the overall build.

When a build starts, the status will be updated, like follows:

Status-3

There’s a gray bar across the top – this will eventually progress as the build is running (at the moment I’ve hard-coded it to 50%).

Clicking on an item will drill down into the details – including (hopefully) the progress through the item:

Status-4

Finally, when everything has completed, the statuses will be updated and the progress bar colour changed to green (for success) or red (for failure).

Status-5

Eventually I’ll also expand it out to the dashboard (that’s a bit harder to do the automatic refreshing). Also the framework is in place for allowing items to define their own details – so the display will become more meaningful.

Anyway, this is just a quick sneak peak at what I’m working on – what do you think of it?

UPDATE

One of the things I didn’t mention in this post, is these new screens are part of CCTray – not a new application (or part of Project Leo). They will be accessed by right-clicking on a project in the list and select Current Status (selecting a project and pressing F4 will do the same).

Categories: CruiseControl.Net Tags:

Implementing Messaging, Part I

2 February, 2009 1 comment

Nibbling a Little Bit at a Time

In my last post (here) I outlined my plans for changing to a messaging-style method invocation. This is purely for the client-server communications, and will hopefully make things easier in the long run.

Well, I have started on an implementation of these plans. There are a total of 37 different operations that can be called on ICruiseManager (some of which have different overloads, which brings the total up to around 50). Trying to do all of these in one sitting would be a lot of work, but make it harder to guarantee everything works! Therefore, given the huge scope I’ve decided to nibble a little bit at a time.

My plan, in broad strokes, is as follows:

  1. Implement a proof-of-concept using the start and stop project methods
  2. Convert all methods that were modified for security (e.g. force build, abort build, request, etc.)
  3. Convert all of the methods that were added for security (e.g change password, read audit events, etc.)
  4. Convert the remaining read-only methods (e.g. that retrieve project and status information)
  5. Convert the configuration modification methods (e.g. add project, delete project, etc.)
  6. Build the common communications library (which will need planning as well)

This breaks things down into manageable chunks. Now I probably won’t post on all of these chunks – as most of it will just be grunt work. Instead I’ll just post on some of the notable points and the lessons I learn along the way.

Step 1: Some New Classes

The very first step, before I even modify the methods, is to add some request and response classes. Since these are very generic methods, I’ve added the ServerRequest, ProjectRequest and Response classes from my plan. These classes are also very generic.

A ServerRequest contains the basic details for a request to a server. Every method needs to have at least this as the base message. This contains a timestamp, an identifier, name of the target server and the session token.

The first two properties will be used to prevent replay attacks. This is where a message is intercepted and sent to the server multiple times. While this is unlikely to bring down CC.Net (due to the way it queues requests) it could force the server to work harder than it needs to (i.e. constantly processing force build requests). These two properties limit replay attacks. I only say limit at the moment, since the messages are all sent plain text. Therefore a smart hacker could intercept a message and change these two properties for each replay! I’ll do some more on preventing this scenario later on.

The name of the target server is literally that – which server is intended to process this message. This is mainly for HTTP scenarios where multiple CC.Net servers are behind an HTTP facade.

Finally the session token is common to the security changes. The main thing to note here is I want every message to include the session token. This is a first step towards being able to completely lock down a server (e.g. even including hiding projects!)

The one property missing from ServerRequest that is often needed is the project name. This is because the project name is not required for every single request. To allow for those requests that do need a project there is the ProjectRequest class. This inherits from ServerRequest and adds the project name property.

Both ServerRequest and ProjectRequest are input classes – they send data into the methods. The corresponding output class is Response. This is a basic reply class that lets the client know what happened. As such it has a timestamp and identifier of the request, plus a result code and any error messages.

The result code can be success, failure or warning. Success and failure are fairly straight-forward, the operation either ran or it didn’t. Warning means the operation still ran, but some error messages were generated. As such the error messages need to be used together with the result code.

That describes the basic message classes, time to do something with them.

Step 2: The Contract

In WCF terms the contract is the agreement between the client and the server. It tells each party which operations can be performed and what the input and output messages are. While we are not working on a WCF application we still have the same principal in CC.Net.

Instead of a contract, CC.Net uses the ICruiseManager interface as its agreement. Now, in theory once an interface has been designed and deployed it should never be changed. Changing this interface forces all other software that is built using it to need a rebuild! But since ICruiseManager is mainly an internal interface (i.e. we haven’t documented the API for external use) I’d already modified it to allow security.

I had a chat with some of the other devs about some different approaches to adding and we came down to two:

  • Modify ICruiseManager (and force everyone to rebuild)
  • Add a new interface (and then figure out how to expose it)

In the end we decided on the first approach, mainly because it is simpler and I have already modified it to allow security!

So to use the new messaging classes, I removed all the overrides for Stop() and Start() and replaced each with one method that takes in a ProjectRequest and returns a Response. I also made a similar change to ICruiseServer, since the cruise managers just act as pass-through layers to this interface.

Of course, attempting to compiling the code at this point breaks lots of things!

Step 3: Changing the Server

Now to start fixing everything. Since the server is the heart of CruiseControl.Net, I’ve started there. Since most requests have a similar pattern I’ve made a helper method called ValidateRequest() in CruiseServer. This validates the timestamp and identifier (I’ve also added an identifier cache to store previous identifiers).

Each project request uses the following code:

Response response = new Response(request);
try
{
    // Validate the request and check the security token
    ValidateRequest(request);
    if (permission.HasValue)
    {
        CheckSecurity(request.SessionToken,
            request.ProjectName,
            permission.Value,
            eventType.Value);
    }
    // Perform the actual action
    action(request);
}
catch (Exception error)
{
    response.Result = ReponseResult.Failure;
    response.ErrorMessages.Add(error.Message);
}
return response;

So I extracted this into a separate helper method called RunProjectRequest(). The action() call in the above code is to a delegate (Action<ProjectRequest>), and since C# allows anonymous delegates I can make calls to this method like this:

public Response Start(ProjectRequest request)
{
    Response response = RunProjectRequest(request,
        SecurityPermission.StartProject,
        SecurityEvent.StartProject,
        delegate(ProjectRequest arg){
            // Perform the actual project start
            if (!FireProjectStarting(arg.ProjectName))
            {
                integrationQueueManager.Start(arg.ProjectName);
                FireProjectStarted(arg.ProjectName);
            }
        });
    return response;
}

At the moment I only have the start and stop project methods, but this sets the background for any project-based call.

This is the main change on the server-side (other than modifying the pass-through layers). Now, the next change is to modify the clients to work with the new signatures.

Step 4: Changing the Clients

In CCTray the class that handles .Net Remoting calls for a project is RemotingCruiseProjectManager. This just calls the various methods on ICruiseManager, so I added a helper method called GenerateProjectRequest(). This takes in a session token, adds the project name and generates a complete ProjectRequest.

I also added a second method called ValidateResponse(). This checks the result on the response – if it is failed then it throws an exception (the error messages are joined together and included in the message). This is required so the methods work in the same way as previously.

Then the only change is to modify the StartProject() and StopProject() methods to use these methods. Now they work with the new messaging syntax and still appear to be the same.

I also made similar changes to ServerAggregatingCruiseManagerWrapper for the dashboard. I say similar because the parameters are slightly different (ServerAggregatingCruiseManagerWrapper uses IProjectSpecifier instead of storing project names.) However the method names are the same and the end result it the same.

So that completes the proof-of-concept for starting and stopping projects. A quick test shows that these methods still work, both with and without security.

Where to Next?

Next I need to continue modifying the rest of the methods in ICruiseManager. This may take a while, but hopefully the helper methods will simplify things.

As I previously stated, I listed the chunks that I’m going to work in, but I’m not necessarily going to post on all of them. Hopefully my next post will be to say the conversion is complete, now it’s time to look at the Common Communications Library :-)

Stay tuned…

Big Visible Cruise (BVC)

9 January, 2009 2 comments

Another CruiseControl Front-end

I was talking with one of the other devs on CruiseControl.Net and he pointed me in the direction of http://code.google.com/p/bigvisiblecruise/. This is a WPF front-end that displays the current build status of all projects on a server – and it does it using a really simple approach that makes it easy to see at a glance what is happening.

Now, I haven’t tried it yet (struggling with the installer at the moment), but I’ll give it a try at work sometime.

Stay tuned…

Passing Dynamic Parameters: Part 2, CCTray

Static No More

In my last post (read it here) I modified the server component of CruiseControl.Net so dynamic parameters can be set for projects and tasks. However, there’s no point in allowing dynamic parameters if the end user can’t set them.

In this post I’ll modify CCTray so the user can set the dynamic parameters.

What Can the User Do?

The first part of allowing the user to set parameters is to find out what is allowed. This involves a call to the server to retrieve the list of parameters, and then displaying them.

The interface that allows connection to the server is ICruiseProjectManager. I’ve added a new method called ListBuildParameters(). The controller will call this method to see if there are any parameters. If so it will display them so the user can change them. Otherwise it will just perform a force build.

I also modified IProjectMonitor so it has the same method. This is because the controller doesn’t directly call ICruiseProjectManager, instead it uses this interface (as a pass-through layer). This also required modifying AggregatingProjectMonitor, ProjectMonitor and SynchronizedProjectMonitor (these all implement the interface).

The final step is to modify HttpCruiseProjectManager and RemotingCruiseProjectManager to actual populate the parameters list. Since RemotingCruiseProjectManager just uses .Net Remoting, this was nice and simple – just called the new method on ICruiseManager. HttpCruiseProjectManager is a bit harder since it also needs changes to the dashboard, so I’ll cover this in my next post.

Displaying the Parameters

Now I have the parameters, the next stage is to display them. I’ve added a new form that contains a PropertyGrid for the display and a couple of buttons.

The grid will show the parameters and allow the user to change them. Clicking on the build button starts the force build (using the controller) and passes in the parameters as a Dictionary<string, string>.

Now I would love to say it was nice and easy to display the parameters in the grid, but it wasn’t :( When I had built a project previously using .Net 1.1 I had used a ICustomTypeDescriptor implementation together with the PropertyGrid to allow some custom processing. This time, using .Net 2.0, I just couldn’t get it to work (the values were read-only, nothing I did would make them writable!)

Now the funny thing is, PropertyGrid works fine without the custom type descriptor – there is obviously some “special” processing going on to allow this to work (a CLR bug?) So I decided to generate my own property classes using System.Reflection.Emit. This is fairly simple to do, especially with the help documentation.

So now I have a rather hack way of displaying the parameters to the user, but it works:

clip_image002

In this example screen shot I have my three parameters, with nice display names and a description.

clip_image003

I’ve even generated the parameters with allowed values as enumerations so the dropdowns will pre-populate. And of course, since I generate the custom class the numeric values include validation.

Sending the Values

The final step in the process is to pass the parameters to the server. The new form already generates the parameters in a dictionary for the server, so it just needs to be passed on.

This also involved a change to ICruiseProjectManager and IProjectMonitor (and their associated implementations). I simply added a new parameter to the ForceBuild() to take in the dictionary and pass it on (yes, I will still need to modify HttpCruiseProjectManager when I work on the web dashboard.)

A New Dynamic World

With these changes in place the user can now pass on dynamic values to the server and perform “custom” builds :)

The final step in this project is the dashboard, so I’ll cover this in my final post in this series.

Stay tuned…

Categories: CruiseControl.Net Tags:

HTTP and Tray Icons

13 December, 2008 Leave a comment

Continuing with the Prototype

I’ve been slowly adding my functionality to my CCTray replacement prototype. It’s slowly approaching the stage where I think it will be ready for people to try playing with it, but it’s not quite there yet. In this post, I cover a couple more areas of functionality, so some quick refactoring and discover an issue (resource conflict?)

HTTP Transport

The original CCTray offers two possible transports – .Net Remoting and HTTP (I have added a modification to allow it to handle any number, but it hasn’t made it into the trunk yet). In contrast my new replacement doesn’t have any transports – they need to be added as server monitors (thus allowing any number of possibilities). Currently I’ve already built a .Net Remoting implementation, now it’s time to implement HTTP.

.Net Remoting was very simple – just needed to instantiate a remoting instance of ICruiseManager and then call the relevant methods. Sure I needed to do some translations between their objects and mine, but still very simple.

HTTP is a bit more complicated – everything needs to be converted into HTTP requests and the responses are just plain text (well, actually XML, but close enough). Therefore there’s a lot more work!

But the good news is it has already been done – in the original CCTray application :) So all I did was copy and paste, and then make a couple of modifications for my application.

So, since I’m making this a modular application I started a new project for the monitor, added the basic classes (the IServerMonitor implementation, plus some configuration settings). Then I copied the following classes/interfaces from the original:

  • IDashboardXmlParser
  • DashboardXmlParser
  • IWebRetriever
  • WebRetriever

These classes I copied over unchanged. I also copied over code from HttpCruiseProjectManager and HttpCruiseServerManager into my IServerMonitor implement and then modified them to work with my code.

I did discover one oversight along the way – the dashboard needs the server as well as the project. But this information is not stored anywhere! Instead it is assumed that the server is part of the web URL (I say assumed because this is not always the case!) Thus the HTTP transport needs to parse the URL, retrieve the server name and then pass it in the web request. This would probably be a good area to tidy up in a future patch (but it will need to be at the server side, not the client side).

Otherwise the HTTP transport is now finished :-)

Some Refactoring

In IServerMonitor the calls to ForceBuild(), AbortBuild(), StartProject() and StopProject() all took in a string for the project name. When I discovered the above issue I changed this to take in a ProjectDetails instance.

Initially I made the change with the hope of extracting the server name from the full details, but I discovered that the name isn’t passed down from the server. Instead I now use it for getting the web URL. Since I modified the interface I also needed to modify the .Net Remoting instance, but this was a simple matter of changing it to use the name from the instance.

In the future when the server side is modified to pass the server name (or alias) then this can be used as the information will already be in place.

General Settings

In the configuration window I had added a tab for general settings. This currently has two settings, show in tray and hide when minimised, which currently do nothing.

So I added a NotifyIcon instance to the main window, with an icon and application name, and added some code to show/hide this based on the configuration settings. I also added an event handler to the SizeChanged event – this detects when the window is minimised and if necessary hides the window (very straight-forward stuff).

To the NotifyIcon instance I added a ContentMenuStrip which has a show and an exit command. The exit command just calls Exit() on the controller (thus shutting down the application). If the window is hidden, then the show command restores it to it’s previous state, otherwise it brings the window to the front of the z-order.

An Issue?

While I was adding the general settings I also discovered an issue where one of the monitor processes was crashing. I think this was due to a conflict in writing to the log file, so I added a retry loop (in a try/catch block) with a 10ms delay. After this change the error went away, but it could possibly recur some point – especially if there are lots of server monitors!

My resolution? Currently none :( I don’t know enough about multi-threading and cross-process resource locks. So this will need to a topic for me to research later (unless somebody else know how to do it and can tell me.)

Enough for Now

That’s enough changes for now. I had hoped to work on security, but the HTTP changes took a bit more work than planned (yes, even though I copied the other code). But for my next post I’m planning on covering security (once I’ve thought about it some more).

Stay tuned…

Categories: CruiseControl.Net, Project Leo Tags:
Follow

Get every new post delivered to your Inbox.