Article Figure 1 Listing 1 Listing 2 Listing 3
Listing 4 jul2006.tar

Even Ants Can Be Assertive

Alan M. Berg

Scripts are solid building blocks that can be glued together to create a strong edifice. Once in place, they will run with predictable regularity. However, sometimes things go wrong; directory structures change, someone accidentally unpacks a zip file in the wrong place as root, and Web applications fail to deploy properly. This article follows my previous article "Platform Independent Stress Testing from the Command Line" and emphasizes two themes. The first is how to handle unexpected errors gracefully. The second theme is the use of Ant in an Apache Tomcat environment to centrally control deployment and assertion testing. This second theme follows naturally in the lifecycle of a Web application from stress testing a new infrastructure to more incremental deployments and assertions testing to ensure the deployments are consistently healthy.

Introduction

In the previous article, I described the basics of using the Apache Ant tool [1]. Ant reads build files that have a series of defined tasks. Tasks are succinct and powerful; one task may be for archiving, another for scp or sending of mail. Series of tasks are accumulated into targets. When running Ant, we need to tell Ant which target to run. Previously, I showed how to stress-test systems via Ant and a third-party tool and then generate and publish the results. In this article, I'll take the process further and show how, with the help of the Anteater project [2], we can also perform assertion tests. On the way, I will also describe how to deploy Web applications in Tomcat [3] via Ant and how to template configuration files, such as httpd.conf, to lower the risks of entropy and the entropy-enhanced laws of Murphy.

Figure 1 defines a generic deployment process for Web applications. The process begins in a development environment. After a significant development effort, the beta application is transferred into an acceptance environment where functional tests are performed including assertion tests. Assertion tests ensure that if specific actions take place, specific results are returned. It is considered a good working practice to keep assertion testing away from the developers as the results may be subjectively influenced by their wishful thinking.

Anteater is a feature-rich assertion testing system built on top of Ant. Anteater uses the familiar build file structure. By capturing functional tests in this manner via Anteater, the tests can be scheduled via cron to run at non-intrusive times without human intervention, and the results can be read later. Further, the tests can be made configurable to run against different machines with different expectancies. A lot of time, error, and effort is potentially removed by this ongoing series of actions.

After being accepted, applications are placed into the production infrastructure. Many of the configuration files in development tend to be similar in acceptance and the production environments. For example, the Apache httpd.conf file may be the same in all environments except for performance options and generic properties such as the server name. In fact, it is bad practice not to have such a monoculture, as diversity brings with it extra maintenance issues. By turning configuration files into templates and stamping the template for each environment, we create a consistent set of configurations. This consistency ensures that debugging of issues is easier and reduces the risk of typos. One can imagine a situation where configuration is done on one central machine and the changes are pushed out to the various targets via one master Ant build file. Better still, changes in a central place can be synchronized and stored in CVS.

This article is broken into four main Ant-related themes that support the flow of new applications into production, as defined in Figure 1:

  • Dealing with errors gracefully
  • Templating httpd.conf files
  • Deploying Web applications via Tomcat
  • Assertion testing via Anteater

Dealing with Errors Gracefully

Entropy -- the movement from an ordered system to a chaotic one -- is a significant force in any systems administrator's daily life. Things go wrong; management makes decisions! Hard to believe but it can happen. Hard drives fail, people get sick, directories get removed, and permissions are changed. Certain types of mistakes are almost inevitable and can be planned for. For example, expected settings may be missing in configuration files, directories or files may not exist, scripts may get executed as the wrong user. Even the most hardened systems administrator can forget to su.

Listing 1, example1.xml, employs tactics to deal with potential failure. The listing highlights the generic tactic of first checking for core issues then, if necessary, failing gracefully before doing any significant and everlasting damage that only the backups can repair. Of course, you should not check for any potential error. Over-engineering adds new forms of complexity and entertains the chance of injecting new self-generated errors. However, I think with 10% effort, you can catch 70% of the problems. Of interest in example1.xml are the following lines of code:

<tstamp><format property="today" pattern="yyyy_MM_dd_HH_mm"/></tstamp>
            
The property "today" is defined with a timestamp that generates results similar to 2006_04_01_13_09. This pattern lists files in order when using ls if the pattern is used as part of all the file names:

<available property="isDir" file="${dir.location}" type="dir" />
<available property="isFile" file="${file.location}" type="file" />
Checking for the availability of files or directories is achieved via the <available> tag. The type of check is defined by the obviously named type attribute. In the listing the properties "isDir" and "isFile" will only exist if the given file or directory exists.

Next, we need to accumulate the tests into one master control property "isConfigured". This accumulation is achieved via the condition statement:

<condition property="isConfigured">
<and>
    <isset property="isDir" />
    <isset property="isFile" />
    <isset property="property.value" />
    <not>
        <equals arg1="${user.name}" arg2="root" casesensitive="false"/>
    </not>
</and>
</condition>
"isConfigured" will only be defined if the properties "isDir", "isFile", and "property.value" exist. "property.value" is a configuration setting read in from example1.properties. Note the use of the <not> tag. At this point you may be wondering where the variable user.name derives from. Ant has a number of built-in properties. The user.name, as you would expect, is the username of the owner of the process running. Check the target "info" for more examples of these built-in variables. The <equals> tag tests the value in user.name against the case-insensitive value "root". "isConfigured" is set when the file and directory exist, the "property.value" is set, and the user is not root. Yes, basic stupidity testing for systems administrators working late on an overloaded and busy Saturday evening.

What I have not shown is that the condition statement allows for other tests as well, including which OS is running, the length of a string, whether a Web page is returned without a 400 error from any given server, and much more [4].

The final piece of the puzzle is the answer to the question of how to fail gracefully. One approach is to call a task that will cause failure if the "isConfigured" property is not defined:

<target name="report_bad_configuration" unless="isConfigured"  >
    <echo>
    System is not configured correctly
        Directory exists ${dir.location} = ${isDir}
        File exists ${file.location} = ${isFile}
        property.value = ${property.value}
    </echo>
    <fail>FAILING GRACEFULLY....</fail>
</target>
The target will not run unless "isConfigured" exists. Notice that we use echo for most of the failure message. This places the information above the BUILD FAILED stanza (as shown next), which is more intuitive to read than below the stanza. Example1.xml should fail generating a result similar to:

Buildfile: /usr/local/eclipse/workspace/sys_admin2/buildfiles/example1.xml
MAIN:
     [echo] Trying to open logfile example1_2006_04_01_13_09_.log
report_bad_configuration:
     [echo] System is not configured correctly
     [echo] Directory exists example1 = true
     [echo] File exists example1/does_not_exist.txt = ${isFile}
     [echo] property.value = 30
 
BUILD FAILED
/usr/local/eclipse/workspace/sys_admin2/buildfiles/example1.xml:33: \
  The following error occurred while executing this line:
/usr/local/eclipse/workspace/ sys_admin2/buildfiles/example1.xml:27: \
  FAILING GRACEFULLY....
If you want all the conditions to be met, then you will need to uncomment the correct value of file.location in the example1.properties file. Note that the build file logs its actions via the record tag.

Templating Httpd.conf

When you have a development, acceptance, and production life cycle, then you will have three slightly different sets of configuration. For example, the main Apache [5] configuration file, httpd.conf, may have different server name and IP addresses to listen to, perhaps tweaks in the performance setting, and occasionally extra modules need to be activated. Instead of a scatological approach, you may consider using one template file that is stamped with specific properties for each environment.

Advantages of this approach include a central repository where you can consistently push out changes and view all the property value. With this extra stability, configuration files are less prone to last-minute editing errors. Imagine making changes rapidly in a Monday evening maintenance slot on your production systems. It is easier to change a property file of ten lines and run Ant than to look through the 300 lines of target configuration, mostly comments to zoom on a specific detail. You can do that, but opportunity knocks for extra downtime.

The core idea for templating is that Ant has a copy command that can search the copied files for specified tokens. If the tokens arise, they can be replaced by variables defined in property files. Listing 2 performs such a task. A template file is copied to a target directory and stamped. The values that are set depend upon the property type in the example2._type.properties file. By changing type, we can change which properties are set and hence have different values for development, acceptance, or production. The key lines of code are:

<format property="now" pattern="MM/dd/yyyy hh:mm aa"/></tstamp>
The property "now" has a human-readable time stamp that later in the script will by added as part of a comment to the top of the httpd.conf file:

<property file="properties/example2_name.properties"/>
<property file="properties/example2_${type}.properties"/>
The property type in example2_name.properties defines which property file the rest of the variables are loaded from. So, for example, we may have type dev=development or acc=acceptance.

HINT: If your structure is dissimilar qua operating systems (e.g., department A uses Apache on Windows and department B uses Unix), then you may also consider locally templating the file and using a property file based on OS. This can be achieved via Ants built-in variables, for example:

<property file="${os.name}.properties"/>
To add a token to the template file, surround the variable name with @@ (e.g., @EXTENDED.STATUS@). In the script itself, the following lines of code do the lifting work:

<filter token="EXTENDED.STATUS" value="${extended.status}"/>
 
<copy todir="example2/target" filtering="true" overwrite="true">
    <fileset dir="example2/template" includes="httpd.conf" />
    <globmapper from="*" to="*.${type}"/>
</copy>
The filter tag defines the token to be replaced. The copy tag does most of the work. Fileset defines the files to be copied, and the globmapper copies file x with the extension of the value defined in the variable type (e.g., ${type}=dev then httpd.conf is copied to httpd.conf.dev).

You may well be thinking that this method is useful for single properties, but how about enabling or disabling whole features, such as an extra virtual host or functionality like PHP? Well, this turns out to be reasonably simple. Suppose you have in httpd.conf the token PHP.ENABLE, for example:

@PHP.ENABLE@
You may add the following filter to the script just before the copy tag:

<filter token="PHP.ENABLE" value="${php.enable}"/>
In the dev property file, you could include the line:

php.enable=Include /usr/local/apache/conf/php.w32.conf
And create the php.w32.conf file with content similar to:

LoadModule php5_module "d:/php/php5apache2.dll"
AddType application/x-httpd-php .php
PHPIniDir "d:/php"
In production, enabling PHP may not be valid, so you would add a value roughly equivalent to:

php.enable=#PHP is not enabled in the production environment
In this section, we have seen that it is possible to template important and complex configuration files via Ant allowing consistency and overview across infrastructure boundaries. In the next section, we shall see how simple it is to deploy Web applications to Tomcat.

Web Application Deployment for Tomcat

Deployment of Web applications via war files under the control of Tomcat is straightforward. A number of approaches are possible. You could, for example, copy the war file directly to the webapps directory of the target Tomcat server and wait for the war file to be automatically expanded and deployed. You could also place an expanded war file in the webapps directory, stop the server, delete the work directory, and start the server. You could log in to the built-in management application found under the URI /manager and upload the war file or even use an Ant task.

Each approach has pros and cons. For example, you may consider having the management application installed as a potential security risk. Or, more likely, the administrator wants to verify by hand every potentially dangerous action. Whichever approach you use, you should verify that the action has taken hold. In this section I will mention the Ant task for deployment, and in the next section I will discuss assertion testing via Anteater.

Listing 3 uses a Tomcat-specific Java library to define a new set of tasks. The library that you will need for this task may be found relative to the home directory of any installed Tomcat 5 server at server/lib/catalina-ant.jar. The taskdef verb loads in a property file with a series of tasks pointing to the related Java class in the Catalina-ant jar file. The pathelement attribute defines where the library may be found within the file system:

<taskdef file="properties/example3_tasks.properties">
      <classpath><pathelement path="${lib.file}"/></classpath>
</taskdef>
In the property file, example3_tasks.properties, the deploy tag (among others) is related to a specific Java class found in the Java library mentioned:

deploy=org.apache.catalina.ant.DeployTask
Example3.xml deploys the war file, stops the Tomcat servers, starts the server, lists the running applications and the context they live under, removes the application, then again lists contexts. A typical tag is the deployment tag; notice that the name of the application is allowed to be different from the filename of the archive:

<deploy url="${manager.url}"
           username="${user}"
            password="${pass}"
            path="/${war.name}"
            war="file:${war.location}"/>
An important note on security -- always pass to the Ant build script the username and password for the admin user of the Tomcat manager application via the -D command. This is a better practice than carelessly leaving the information around on your file system. For example:

ant -Duser=admin  -Dpass=secret  -f build.filename
In this section I have shown you how straightforward it is to deploy and maintain Web applications via Ant for Tomcat servers. Assertion testing will be described next.

Assertion Testing via Anteater

Assertion testing with Anteater is surprisingly simple. Using build files with elementary tasks, you can quickly generate a whole range of test actions that can, via property files, be fired off in the direction of any arbitrary piece of Web-enabled infrastructure. In this section I will show you a basic test for a login page. This gives you a succinct hint of the potential of Anteater.

Anteater, however, can do more. You can, for example, turn on Anteater's own Tomcat server and let the tool listen for requests and test those requests against expected values. A prime example of this features value is in Web service testing. In the world of Service-Oriented Architecture, where one server may be a facade for accumulating other services, this may become a priority feature.

Anteater is a self-contained package with the required Ant libraries included. To install, download the package. Unzip and then add the underlying bin directory to the PATH environment variable. Running anteater -f buildfile.name should work now. Anteater relies on included Ant library files to work. However, if you want to run Anteater files from within your standard Ant package, you will need to refer to the Anteater user manual [6].

Listing 4, example4.xml, runs three tests against the simple application. You will find the application under the buildfiles/example3/simple.war directory in the source code included with this article. Describing example4.xml: taskdef defines the Anteater task and needs to be included in all build files:

<taskdef resource="META-INF/Anteater.tasks"/>
<typedef resource="META-INF/Anteater.types"/>
Anteater uses the concept of groups to configure different parts of a test for different ranges of internal settings. Anteater has a standard set of control properties. For example, haltonerror halts the build file if there is an error found by a given task. Some tasks you may want to be able to halt the show, but others not. Therefore you would define one group with haltonerror=true and another with haltonerror=false. For a full list of defaults that can be altered, see references [7]:

<group id="local">
  <property name="haltonerror" value="false" />
  <logger type="xml" todir="${log.dir}"/>
</group>
The greatest work is done via the http tag. For example, the following snippet posts to the URL defined by the variable url_parser. The method used is POST. The request sends the parameters "user" and "pass" and expects a response of 200, but no content with the string Error in it:

<httpRequest href="${url_parser}"  group="local" >
  <method>POST</method>
  <parameter name="user" value="tester1" />
  <parameter name="pass" value="badpassword" />
<match>
  <responseCode value="200"/>
  <regexp mustMatch="false" assign="n">Error</regexp>
</match>
</httpRequest>
The first point to note here is that error handling is specific to any given application -- hence Anteater's need to error check for a string as well as the standard return codes. The second point is that there is an implicit assumption in the test that the account user tester1 exists. The code snippet has the test account hard-coded in. Best practices suggest that the account should be placed in a property file, ready for alteration to match any testable structure later.

Generating the report is handled by a built script. The anteater.report location is built in and points to a build file that performs an XSL transformation on the XML result file. This task results in a set of easily readable HTML pages:

<ant antfile="${anteater.report}">
      <property name="log.dir" location="${log.dir}"/>
      <property name="report.dir" location="${report.dir}"/>
</ant>
Conclusions

Ant is a powerful tool for doing heavy lifting within complex infrastructures. By centralizing configuration control and deployment, you have a greater chance of consistency and thus predictability of outcome. Error checking of common faults such as missing configuration files or running under the wrong user helps in the fight against entropy.

Resources

1. Ant homepage -- http://ant.apache.org/

2. Anteater homepage -- http://aft.sourceforge.net/index.html

3. Tomcat homepage -- http://tomcat.apache.org/

4. Ant condition tests -- http://ant.apache.org/manual/CoreTasks/conditions.html

5. Apache homepage -- http://www.apache.org/

6. Anteater tasks from within Ant -- http://aft.sourceforge.net/manual/Invoking%20from%20Ant.html

7. Anteater group details -- http://aft.sourceforge.net/manual/Auxiliary%20tasks.html#elem:group

Alan M. Berg has been a lead developer at the Central Computer Services at the University of Amsterdam for the past seven years. He likes to get his hands dirty with the building and gluing of systems. In previous incarnations he was a technical writer, an Internet/Linux course writer, and before that a science teacher. He remains agile by playing computer games with his kids (Nelson and Lawrence) who sadly consistently beat him.