add support for a consul backing store for trackig provisioned state
diff --git a/automation/Godeps/Godeps.json b/automation/Godeps/Godeps.json
index c55b337..5e6d307 100644
--- a/automation/Godeps/Godeps.json
+++ b/automation/Godeps/Godeps.json
@@ -18,6 +18,11 @@
 			"ImportPath": "gopkg.in/mgo.v2/bson",
 			"Comment": "r2015.12.06-2-g03c9f3e",
 			"Rev": "03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64"
+		},
+		{
+			"ImportPath": "github.com/hashicorp/consul",
+                        "Comment": "v0.6.4-341-g22938ab",
+                        "Rev": "22938ab8b3ca069e490a4897a8ec13308ac61605"
 		}
 	]
 }
diff --git a/automation/tracker.go b/automation/tracker.go
index 9f38b23..e299a68 100644
--- a/automation/tracker.go
+++ b/automation/tracker.go
@@ -3,9 +3,11 @@
 import (
 	"encoding/json"
 	"github.com/fzzy/radix/redis"
+	consul "github.com/hashicorp/consul/api"
 	"log"
 	"net/url"
 	"os"
+	"strings"
 )
 
 type ProvisionState int8
@@ -48,6 +50,46 @@
 	Clear(key string) error
 }
 
+type ConsulTracker struct {
+	client *consul.Client
+	kv     *consul.KV
+}
+
+func (c *ConsulTracker) Get(key string) (*TrackerRecord, error) {
+	pair, _, err := c.kv.Get(key, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if pair == nil {
+		var record TrackerRecord
+		record.State = Unprovisioned
+		return &record, nil
+	}
+
+	var record TrackerRecord
+	err = json.Unmarshal([]byte(pair.Value), &record)
+	if err != nil {
+		return nil, err
+	}
+	return &record, nil
+}
+
+func (c *ConsulTracker) Set(key string, record *TrackerRecord) error {
+	data, err := json.Marshal(record)
+	if err != nil {
+		return err
+	}
+	pair := &consul.KVPair{Key: key, Value: data}
+	_, err = c.kv.Put(pair, nil)
+	return err
+}
+
+func (c *ConsulTracker) Clear(key string) error {
+	_, err := c.kv.Delete(key, nil)
+	return err
+}
+
 // RedisTracker redis implementation of the tracker interface
 type RedisTracker struct {
 	client *redis.Client
@@ -77,7 +119,11 @@
 }
 
 func (t *RedisTracker) Set(key string, record *TrackerRecord) error {
-	reply := t.client.Cmd("set", key, true)
+	data, err := json.Marshal(record)
+	if err != nil {
+		return err
+	}
+	reply := t.client.Cmd("set", key, data)
 	return reply.Err
 }
 
@@ -114,8 +160,14 @@
 //            depends on the environment. If a link to a redis instance is defined then this will
 //            be used, else an in memory version will be used.
 func NewTracker() Tracker {
-	// Check the environment to see if we are linked to a redis DB
-	if os.Getenv("AUTODB_ENV_REDIS_VERSION") != "" {
+	driver := os.Getenv("AUTODB_DRIVER")
+	if driver == "" {
+		log.Printf("[info] No driver specified, defaulting to in memeory persistence driver")
+		driver = "MEMORY"
+	}
+
+	switch strings.ToUpper(driver) {
+	case "REDIS":
 		tracker := new(RedisTracker)
 		if spec := os.Getenv("AUTODB_PORT"); spec != "" {
 			port, err := url.Parse(spec)
@@ -124,14 +176,27 @@
 			checkError(err, "[error] unable to connect to redis database : '%s' : %s", port, err)
 			log.Println("[info] Using REDIS to track provisioning status of nodes")
 			return tracker
-		} else {
-			log.Fatalf("[error] looks like we are configured for REDIS, but no PORT defined in environment")
 		}
+		log.Fatalf("[error] No connection specified to REDIS server")
+	case "CONSUL":
+		var err error
+		config := consul.Config{
+			Address: "autodb:8500",
+			Scheme:  "http",
+		}
+		tracker := new(ConsulTracker)
+		tracker.client, err = consul.NewClient(&config)
+		checkError(err, "[error] unable to connect to redis server : 'autodb:8500' : %s", err)
+		log.Println("[info] Using Consul to track provisioning status of nodes")
+		tracker.kv = tracker.client.KV()
+		return tracker
+	case "MEMORY":
+		tracker := new(MemoryTracker)
+		tracker.data = make(map[string]TrackerRecord)
+		log.Println("[info] Using memory based structures to track provisioning status of nodes")
+		return tracker
+	default:
+		log.Fatalf("[error] Unknown persistance driver specified, '%s'", driver)
 	}
-
-	// Else fallback to an in memory tracker
-	tracker := new(MemoryTracker)
-	tracker.data = make(map[string]TrackerRecord)
-	log.Println("[info] Using memory based structures to track provisioning status of nodes")
-	return tracker
+	return nil
 }
diff --git a/build.gradle b/build.gradle
index 5103b76..fcc000e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,28 +26,30 @@
     // Deployment target config file (yaml format); this can be overwritten from the command line
     // using the -PdeployConfig=<file-path> syntax.
     deployConfig = project.hasProperty('deployConfig') ? project.getProperty('deployConfig') : './config/default.yml'
+
+    dockerPath = project.hasProperty('dockerPath') ? project.getProperty('dockerPath') : '$dockerPath'
 }
 
 task buildBootstrapImage(type: Exec) {
-    commandLine '/usr/bin/docker', 'build', '-t', 'cord-maas-bootstrap', './bootstrap'
+    commandLine "$dockerPath/docker", 'build', '-t', 'cord-maas-bootstrap', './bootstrap'
 }
 
 task tagBootstrapImage(type: Exec) {
    dependsOn buildBootstrapImage
-   commandLine '/usr/bin/docker', 'tag', 'cord-maas-bootstrap', "$targetReg/cord-maas-bootstrap:$targetTag"
+   commandLine "$dockerPath/docker", 'tag', 'cord-maas-bootstrap', "$targetReg/cord-maas-bootstrap:$targetTag"
 }
 
 task publishBootstrapImage(type: Exec) {
     dependsOn tagBootstrapImage
-    commandLine '/usr/bin/docker', 'push', "$targetReg/cord-maas-bootstrap:$targetTag"
+    commandLine "$dockerPath/docker", 'push', "$targetReg/cord-maas-bootstrap:$targetTag"
 }
 
 task buildAutomationImage(type: Exec) {
-    commandLine '/usr/bin/docker', 'build', '-t', "cord-maas-automation", "-f", "./automation/Dockerfile", "./automation"
+    commandLine "$dockerPath/docker", 'build', '-t', "cord-maas-automation", "-f", "./automation/Dockerfile", "./automation"
 }
 
 task buildAutomationImageAnsible(type: Exec) {
-    commandLine '/usr/bin/docker', 'build', '-t', "cord-maas-automation:ansible", "-f", "./automation/Dockerfile.ansible", "./automation"
+    commandLine "$dockerPath/docker", 'build', '-t', "cord-maas-automation:ansible", "-f", "./automation/Dockerfile.ansible", "./automation"
 }
 
 task buildAutomationImages {
@@ -57,12 +59,12 @@
 
 task tagAutomationImage(type: Exec) {
     dependsOn buildAutomationImage
-    commandLine '/usr/bin/docker', 'tag', 'cord-maas-automation', "$targetReg/cord-maas-automation:$targetTag"
+    commandLine "$dockerPath/docker", 'tag', 'cord-maas-automation', "$targetReg/cord-maas-automation:$targetTag"
 }
 
 task tagAutomationImageAnsible(type: Exec) {
     dependsOn buildAutomationImageAnsible
-    commandLine '/usr/bin/docker', 'tag', 'cord-maas-automation:ansible', "$targetReg/cord-maas-automation:$targetTag-ansible"
+    commandLine "$dockerPath/docker", 'tag', 'cord-maas-automation:ansible', "$targetReg/cord-maas-automation:$targetTag-ansible"
 }
 
 task tagAutomationImages {
@@ -72,12 +74,12 @@
 
 task publishAutomationImage(type: Exec) {
     dependsOn tagAutomationImage
-    commandLine '/usr/bin/docker', 'push', "$targetReg/cord-maas-automation:$targetTag"
+    commandLine "$dockerPath/docker", 'push', "$targetReg/cord-maas-automation:$targetTag"
 }
 
 task publishAutomationImageAnsible(type: Exec) {
     dependsOn tagAutomationImageAnsible
-    commandLine '/usr/bin/docker', 'push', "$targetReg/cord-maas-automation:$targetTag-ansible"
+    commandLine "$dockerPath/docker", 'push', "$targetReg/cord-maas-automation:$targetTag-ansible"
 }
 
 task publishAutomationImages {
@@ -86,17 +88,17 @@
 }
 
 task buildHarvesterImage(type: Exec) {
-    commandLine '/usr/bin/docker', 'build', '-t', "cord-maas-dhcp-harvester", "./harvester"
+    commandLine "$dockerPath/docker", 'build', '-t', "cord-maas-dhcp-harvester", "./harvester"
 }
 
 task tagHarvesterImage(type: Exec) {
     dependsOn buildHarvesterImage
-    commandLine '/usr/bin/docker', 'tag', 'cord-maas-dhcp-harvester', "$targetReg/cord-maas-dhcp-harvester:$targetTag"
+    commandLine "$dockerPath/docker", 'tag', 'cord-maas-dhcp-harvester', "$targetReg/cord-maas-dhcp-harvester:$targetTag"
 }
 
 task publishHarvesterImage(type: Exec) {
     dependsOn tagHarvesterImage
-    commandLine '/usr/bin/docker', 'push', "$targetReg/cord-maas-dhcp-harvester:$targetTag"
+    commandLine "$dockerPath/docker", 'push', "$targetReg/cord-maas-dhcp-harvester:$targetTag"
 }
 
 // ~~~~~~~~~~~~~~~~~~~ Global tasks ~~~~~~~~~~~~~~~~~~~~~~~
@@ -105,8 +107,8 @@
 task fetch(type: Exec) {
     // this is where we fetch upstream artifacts that we do not need internet for the build phase"
     // Placeholdr example:
-    commandLine "/usr/bin/docker", "pull", "golang:alpine"
-    commandLine "/usr/bin/docker", "pull", "python:2.7-alpine"
+    commandLine "$dockerPath/docker", "pull", "golang:alpine"
+    commandLine "$dockerPath/docker", "pull", "python:2.7-alpine"
 }
 
 // To be used to generate all needed binaries that need to be present on the target
@@ -192,5 +194,4 @@
     def skipTags = [].p(config.seedServer.skipTags)
 
     args = args.p(skipTags.asParam("skip-tags", ",")).p(extraVars.asParam("extra-vars", " ")) << "head-node.yml"
-    println args
 }