Monday, September 12, 2005

Know your tools - an example of Scripting vs Programming

     Knowing the proper tool for the job can be a particularly powerful tool in a developer’s arsenal. Knowing the utilities at your disposal can help to eliminate development time, bugs, and difficult management issues with software solutions. One pitfall that beginner programmers tend to fall into is the desire to write a program for everything.

For example, lets look back to my early programming days. I needed to put together a budget, and eager to use my skills, I wrote a small application that would take in my pay amount and split out my budget. It took me a few hours to write. Of course, being a beginner, I did not take into account changes. So for every change I had to make, I had to do more programming and recompile. Since it was not the most efficient solution, I abandoned that application shortly after. Fast forward to recent years where I once again decided to build a budget solution for myself. Of course this time I just did the whole thing on a spreadsheet, something I probably should have done from the beginning. It just seems to work out better that way. It’s simple, effective, and portable.
     
     Most experienced developers would read that and laugh. Looking back, it is pretty funny and I shake my head every time I think of it, but I am sure most programmers have similar stories. Hindsight is always 20/20. Unfortunately smaller businesses tend to put similar systems in place for themselves. Usually they get a part time “wiz kid” or consultant to come in a build a solution to problems that they are having. While eager to show off their skills, the solution is only short term and not very manageable.

     For another example, lets look at an issue I was faced with very recently. We had a vendor come in and implement a new web based enrollment system for us. Of course, the PHBs were completely sold by the sales guys, and what we got was not what we were sold. The system is incredibly unstable, with constant downtime. Guess who gets to deal with the headache of this? So to eliminate response times to system outages, we needed a system to monitor for downtime to hopefully catch these outages before customers called in complaining. The vendor offered us a solution for yet even more hefty sums of cash. Considering the quality of the system itself, I declined their offer and built one myself.

     Since this is a web-based solution, I was able to put together a small (less than 50 lines) Bash script using Curl and Grep to monitor if the page is available, and Mutt to alert us if the system is down. While other utilities are available, Curl was the first tool tested that would work with the single sign-on system correctly. The logic is as follows:

Use Curl to login to site
     If timeout, flag error
Check resulting home page results using Grep.
     If resulting page does not contain certain tokens, flag error.
Check error flag
     If error
           Check if email has been sent
                If no, send email
                If yes, do nothing
      Else
          Check if email has been sent
                If Yes, send email notifying the system is back up
                If no, do nothing.

The script itself looks something like this (Note: comments and echo tags for logging have been removed for brevity):
#/bin/bash

ERROR_FLAG=0

/usr/bin/rm /tmp/curl_output.txt

/usr/bin/curl -o /tmp/curl_output.txt --connect-timeout 30 --max-time 30 -k -L  -d USER=USERNAME -d PASSWORD=PASSWORD -d target=URL_ENCODED_TARGET_FOR_SITEMINDER -d smauthreason=%24%24smauthreason%24%24 -c cookies_file https://server

if [ $? -ne 0 ] ; then
          /usr/bin/echo Date run: `/usr/bin/date` > /tmp/error_report.txt
          /usr/bin/echo "Connection times out after 30 seconds" >> /tmp/error_report.txt
          ERROR_FLAG=1
     else
          if /usr/bin/grep -q "<html><head><title>Page Title" /tmp/curl_output.txt
               then
                    /usr/bin/echo "Token was found, command executed successfully"
               else
                    ERROR_FLAG=1
                    /usr/bin/echo Date run: `/usr/bin/date` > /tmp/error_report.txt
                    /usr/bin/echo "Output page is not correct:" >> /tmp/error_report.txt
                    /usr/bin/cat /tmp/curl_output.txt >> /tmp/error_report.txt
          fi
fi

if [ "$ERROR_FLAG" -eq 1 ] ; then
     if [ -f /tmp/mail_sent ]
     then
          /usr/bin/echo "We do nothing here, file is already sent"
     else
           /usr/bin/cat /tmp/error_report.txt | /usr/bin/mutt -s “ERROR:System-`/usr/bin/date +%F`” people@host.com     
          /usr/bin/touch /tmp/mail_sent      
     fi
else
     if [ -f /tmp/mail_sent ]
     then
          /usr/bin/echo "Server is back up!" | /usr/bin/mutt -s "ERROR:System-`/usr/bin/date +%F`" people@host.com
          /usr/bin/rm /tmp/mail_sent
                 
     fi
fi

This approach has several advantages over writing a compiled program to do the same thing. The components for making and retrieving web pages, store cookies, pass authentication tokens, and fail on timeout already exist in Curl, so this saves on development time. Also, the ability to search through the resulting page already exists in Grep. These are already proven tools that have gone through extensive testing and debugging and have withstood the test of time, so here we are saving time by utilizing tools that are far more reliable than anything we would develop for the same purpose. Due to this time saving, this was out the door in less than two days.

Second, scripting languages tend to be more high level, and much easier to understand. In using a scripting language, not only do I leave maintenance open for developers, but also system administrators, and possibly even power users who inherit this headache. Anyone who is familiar with the environment can go in and make changes or additions as needed. In a later version of this script, I added parameters, making it adaptable to other systems.

There are 3 very important design principles demonstrated from this approach. One is that this tool does one thing and does it very well. Second is that it is easily readable and maintainable by humans. And finally, although more theoretical, it makes use of the reuse concept by leveraging tools that someone else has already written, debugged, and tested. While this is not as “sexy” as a custom solution in C++, C#, Java or whatever the programming language flavor of the month is, it does its job just as well, and in a lot less time.
     
     The point is, know your tools and how to utilize them effectively. Don’t fall into the line of thinking that just because you can write a program for something that it is the most efficient solution to a problem. You can save yourself a lot of time and headache.

No comments: