updated with changes to support automated triggering of post-deploymet provisioning
diff --git a/automation/tracker.go b/automation/tracker.go
new file mode 100644
index 0000000..9f38b23
--- /dev/null
+++ b/automation/tracker.go
@@ -0,0 +1,137 @@
+package main
+
+import (
+	"encoding/json"
+	"github.com/fzzy/radix/redis"
+	"log"
+	"net/url"
+	"os"
+)
+
+type ProvisionState int8
+
+const (
+	Unprovisioned ProvisionState = iota
+	ProvisionError
+	Provisioning
+	Provisioned
+)
+
+func (s *ProvisionState) String() string {
+	switch *s {
+	case Unprovisioned:
+		return "UNPROVISIONED"
+	case ProvisionError:
+		return "PROVISIONERROR"
+	case Provisioning:
+		return "PROVISIONING"
+	case Provisioned:
+		return "PROVISIONED"
+	default:
+		return "UNKNOWN"
+	}
+}
+
+// TrackerRecord state kept for each node to be provisioned
+type TrackerRecord struct {
+	State ProvisionState
+
+	// Timeestamp maintains the time the node started provisioning, eventually will be used to time out
+	// provisinion states
+	Timestamp int64
+}
+
+// Tracker used to track if a node has been post deployed provisioned
+type Tracker interface {
+	Get(key string) (*TrackerRecord, error)
+	Set(key string, record *TrackerRecord) error
+	Clear(key string) error
+}
+
+// RedisTracker redis implementation of the tracker interface
+type RedisTracker struct {
+	client *redis.Client
+}
+
+func (t *RedisTracker) Get(key string) (*TrackerRecord, error) {
+	reply := t.client.Cmd("get", key)
+	if reply.Err != nil {
+		return nil, reply.Err
+	}
+	if reply.Type == redis.NilReply {
+		var record TrackerRecord
+		record.State = Unprovisioned
+		return &record, nil
+	}
+
+	value, err := reply.Str()
+	if err != nil {
+		return nil, err
+	}
+	var record TrackerRecord
+	err = json.Unmarshal([]byte(value), &record)
+	if err != nil {
+		return nil, err
+	}
+	return &record, nil
+}
+
+func (t *RedisTracker) Set(key string, record *TrackerRecord) error {
+	reply := t.client.Cmd("set", key, true)
+	return reply.Err
+}
+
+func (t *RedisTracker) Clear(key string) error {
+	reply := t.client.Cmd("del", key)
+	return reply.Err
+}
+
+// MemoryTracker in memory implementation of the tracker interface
+type MemoryTracker struct {
+	data map[string]TrackerRecord
+}
+
+func (m *MemoryTracker) Get(key string) (*TrackerRecord, error) {
+	if value, ok := m.data[key]; ok {
+		return &value, nil
+	}
+	var record TrackerRecord
+	record.State = Unprovisioned
+	return &record, nil
+}
+
+func (m *MemoryTracker) Set(key string, record *TrackerRecord) error {
+	m.data[key] = *record
+	return nil
+}
+
+func (m *MemoryTracker) Clear(key string) error {
+	delete(m.data, key)
+	return nil
+}
+
+// NetTracker constructs an implemetation of the Tracker interface. Which implementation selected
+//            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") != "" {
+		tracker := new(RedisTracker)
+		if spec := os.Getenv("AUTODB_PORT"); spec != "" {
+			port, err := url.Parse(spec)
+			checkError(err, "[error] unable to lookup to redis database : %s", err)
+			tracker.client, err = redis.Dial(port.Scheme, port.Host)
+			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")
+		}
+	}
+
+	// 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
+}