CruiseControl.NET – vNext – Universal Naming
Another area that is challenging in the current version of CruiseControl.NET is communications. To add a new message that will be used in communicating with a server requires modifications in multiple places – both on the server and the client (although some of the changes made in the past couple of years has made this easier on the client side.)
In addition CruiseControl.NET only supports one protocol for talking to the server – .NET Remoting. There is a web-based protocol but this requires an installation of the dashboard which acts as an intermediate. And as the bug reports show this has caused some other problems. There is an WCF extension that can be used but because extensions were tacked on later not many people know about them – especially as they are configured via the app.config instead of ccnet.config!
For vNext I’d like to take this another step further and abstract away more of the communications infrastructure. This will allow for additional communications protocols natively on the server. Plus we can handle from the start some of the issues that have been raised in the current web-protocol.
These changes will involve a number of different components which I’m planning on covering in the next few posts. In this post I’m going to start on a simple that allows a lot of functionality: universal naming.
Currently we have a bit of a mixed bag with the current versions – we can access some items remotely; others are more hidden. For example it is very easy to access and work with projects – the ever present project name servers as a very useful identifier. It is possible to get some information about queues and tasks but normally this is accessed via a higher level item (e.g. the server snapshot or via a project.) If I wanted to find out how an MSBuild task on a project is configured I would have to get the whole project’s configuration and then extract just the task I wanted – not very obvious.
What I would like to propose is a universal naming standard that allows almost any item within the server to be addressed. This will make it different from how things currently work but it will allow much more control over what is accessed.
The base of this naming is to use non-standard URNs. These will be non-standard in they will not use an officially defined namespace id but otherwise will work just like a standard URN. The namespace will be ccnet – so all identifiers will begin “urn:ccnet:”. As per RFC 2141 “urn” is case-insensitive and “ccnet” will be also.
The generic form of a ccnet urn is:
urn:ccnet:server:path-to-item:item
server and item will always be required. server is either the unique name of a server or its address. For example for the server ccnetlive located at http://ccnetlive.thoughtworks.com server would be either http://ccnetlive.thoughtworks.com or ccnetlive. If ccnetlive is used then the mapping to the URL will need to be stored somewhere else (more on this in a later post.)
item is the unique name of an item. Which item depends on the path that has been entered. To help with resolving an item identifier I think we need a few rules around names within the configuration:
- All item names within a level of configuration must be unique
- All projects must have unique names that cannot be the same as another root-level item
- All project items must have either a single instance or a unique name
Rule #1 basically means that at each level the name will identify a unique item. For example a level might be the root level of the server configuration or it might be the child items within a queue, tasks within a project, etc.
Rule #2 means we have an easy way to identify requests for projects (more on this in a minute.) Ensuring that project names do not exist at the root (unless the project is at the root) removes any confusion between referring to a project or a root level item.
Finally rule #3 means we can refer to individual items within a project. The single instance part means if an item is not named then there cannot be any other instances of that item type. For example if there is an unnamed MSBuild task then there cannot be any other MSBuild tasks within that project or project item.
Returning back to the URNs if we had a project called ccnet-trunk on ccnetlive the basic URN would be:
urn:ccnet:ccnetlive:ccnet-trunk
The final part of the URN is the most interesting part – a path to an item. Coming back to our earlier example of an MSBuild task within a project we could reference this task by using the project name as the path:
urn:ccnet:ccnetlive:ccnet-trunk:msbuild
In this example the path is “ccnet-trunk”. The power of this comes when we start looking beyond just the simple project/task structures. For example if we wanted to look at a queue within a queue we could have something like:
urn:ccnet:server:outerqueue:innerqueue
Here the item is innerqueue and the path to it is outerqueue. This means only projects need unique names – other structural items can have duplicate names as long as they are not duplicated under an item – we could have another item called innerqueue that exists under an item called roundrobin for example.
We could also use this syntax for looking at items that belong to other items. If we had an MSBuild task within a Sequence we could use:
urn:ccnet:ccnetlive:ccnet-trunk:sequence:msbuild
This syntax would allow us to access any part of a server’s configuration easily and consistently.
There is one additional area that needs addressing and that is for individual builds. For example we might want to retrieve the status of a build of a project. Now this by itself has a couple of issues: first the build is more of a filter rather than an actual part of the address and second there is currently no easy way to address every build. Projects do have labellers that generate unique labels for a build, but these are only guaranteed unique for successful builds (i.e. the labels can be re-used from failed builds.) To get around this the current version uses a combination of a date/time stamp, the build status (passed/failed) and the label for uniquely identifying a build!
So to address the first issue I think we should use a different symbol for identifying the build, and I am proposing “->”, this would be appended to the end of the URN. For the second issue I think we should use either the date/time stamp or the build label for the name of a build.
For example if there was a build for the above project that started at 13:02:14 on 12-Dec-2010 we could use the following URN for the build:
urn:ccnet:ccnetlive:ccnet-trunk->20101212130214
The date/time stamp should only go to the nearest second (otherwise it gets too long) and we should enforce that a project cannot start building more than once a second.
Alternately if the build was successful and generated a final build label of 2.0.1 then we could use:
urn:ccnet:ccnetlive:ccnet-trunk->2.0.1
This would also be usable with tasks and other project items:
urn:ccnet:ccnetlive:ccnet-trunk:msbuild->2.0.1
Using the alternate symbol (->) instead makes it easier for address resolution. If we used colons for both the build and the item name when we would need to do a look-up to decide whether it is a build name or otherwise.
So this post has covered a naming standard we can use for addressing different parts of a server. As a preview there are two additional parts I’m planning on covering for communications:
- The Three External Methods: looking at how we can condense our external surface to use three methods (List, Query and Invoke)
- Invokable Methods: looking at how we can expose any method on any item so it can be invoked by an external client
So if you don’t know why I want to add universal naming to vNext stay tuned and (hopefully) things will become clearer.
And as always, I’m happy for feedback, so let me know what you think.
Why not have two identifiers: one label (which is what you have currently) and a new build number. It really feels like there should be two.
When you say two identifiers – do you mean including both in the name (e.g. something urn:ccnet:ccnetlive:ccnet-trunk->21:2.0.1) or have them as alternate ways of referencing:
urn:ccnet:ccnetlive:ccnet-trunk->2.0.1
urn:ccnet:ccnetlive:ccnet-trunk->21
The first approach is what we currently have in CC.NET and it feels clunky (mainly because it is an artificial number). I was thinking it would be nice to have something that us humans could generate if we were keen enough.
Craig
Devils Advocate here again
I’m looking forward to the next post to see more practical examples. I get the general idea though.
You mention : ‘use the following for getting the info of a certain build
urn:ccnet:ccnetlive:ccnet-trunk->20101212130214′
Now I would not do this : daylight savings!
Clock is set back 1 hour once a year
The current code uses the combination of those 3 items you mentioned to bypass the daylight savings problem
We could use the above as a ‘visual’ element, because in 99.9999% of the cases it will be ok. But for the real urn, why not just use a GUID?
ccnet:ccnetlive:ccnet-trunk->22A5B515-EC60-4513-B816-820ED195D7E6
This would be used at the code level, but on screens we can show a more user friendly description of this urn ccnet:ccnetlive:ccnet-trunk->20101212130214
I think this would also resolve some other problems :
° time adjustments on the build server (time sync)
° sort/group build information
With the guid we have a unique name, and if we place this in a struct, it can have properties like date-time, status, label, …
And it would surely make each build uniquely identifiable
Unless we do not really care for these small problems of course.
It dosn’t really matter what we use as the unique build id – as long as we can guarantee that it is unique for build. We could use GUIDs or UTC dates – either would be fine
Craig