updated switchq to not use local storage and attempt to keep two stores in sync

Change-Id: If137abb83099ea3038e8f14cdde7a3d21a936bb3
diff --git a/switchq/switchq.go b/switchq/switchq.go
index a4bec2e..46e68b7 100644
--- a/switchq/switchq.go
+++ b/switchq/switchq.go
@@ -25,7 +25,6 @@
 
 type Config struct {
 	VendorsURL      string `default:"file:///switchq/vendors.json" envconfig:"vendors_url"`
-	StorageURL      string `default:"memory:" envconfig:"storage_url"`
 	AddressURL      string `default:"file:///switchq/dhcp_harvest.inc" envconfig:"address_url"`
 	PollInterval    string `default:"1m" envconfig:"poll_interval"`
 	ProvisionTTL    string `default:"1h" envconfig:"provision_ttl"`
@@ -37,61 +36,80 @@
 	LogFormat       string `default:"text" envconfig:"LOG_FORMAT"`
 
 	vendors       Vendors
-	storage       Storage
 	addressSource AddressSource
 	interval      time.Duration
 	ttl           time.Duration
 }
 
+const (
+	Pending TaskStatus = iota
+	Running
+	Complete
+	Failed
+)
+
+type RequestInfo struct {
+	Id           string `json:"id"`
+	Name         string `json:"name"`
+	Ip           string `json:"ip"`
+	Mac          string `json:"mac"`
+	RoleSelector string `json:"role_selector"`
+	Role         string `json:"role"`
+	Script       string `json:"script"`
+}
+
+type TaskStatus uint8
+
+type WorkRequest struct {
+	Info   *RequestInfo
+	Script string
+	Role   string
+}
+
+type StatusMsg struct {
+	Request   *WorkRequest `json:"request"`
+	Worker    int          `json:"worker"`
+	Status    TaskStatus   `json:"status"`
+	Message   string       `json:"message"`
+	Timestamp int64        `json:"timestamp"`
+}
+
 func checkError(err error, msg string, args ...interface{}) {
 	if err != nil {
 		log.Fatalf(msg, args...)
 	}
 }
 
-func (c *Config) getProvisionedState(rec AddressRec) (int, string, error) {
+func (c *Config) getProvisionedState(rec AddressRec) (*StatusMsg, error) {
 	log.Debugf("Fetching provisioned state of device '%s' (%s, %s)",
 		rec.Name, rec.IP, rec.MAC)
 	resp, err := http.Get(c.ProvisionURL + rec.MAC)
 	if err != nil {
 		log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
 			rec.Name, rec.IP, rec.MAC, err)
-		return -1, "", err
+		return nil, err
 	}
 	if resp.StatusCode != 404 && int(resp.StatusCode/100) != 2 {
 		log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
 			rec.Name, rec.IP, rec.MAC, resp.Status)
-		return -1, "", fmt.Errorf(resp.Status)
+		return nil, fmt.Errorf(resp.Status)
 	}
 	defer resp.Body.Close()
 	if resp.StatusCode != 404 {
 		decoder := json.NewDecoder(resp.Body)
-		var raw interface{}
-		err = decoder.Decode(&raw)
+		var status StatusMsg
+		err = decoder.Decode(&status)
 		if err != nil {
 			log.Errorf("Unmarshal provisioning service response for device '%s (%s, %s)' : %s",
 				rec.Name, rec.IP, rec.MAC, err)
-			return -1, "", err
+			return nil, err
 		}
-		status := raw.(map[string]interface{})
-		switch int(status["status"].(float64)) {
-		case 0, 1: // "PENDING", "RUNNING"
-			return int(status["status"].(float64)), "", nil
-		case 2: // "COMPLETE"
-			return 2, "", nil
-		case 3: // "FAILED"
-			return 3, status["message"].(string), nil
-		default:
-			err = fmt.Errorf("unknown provisioning status : %d", status["status"])
-			log.Errorf("received unknown provisioning status for device '%s (%s)' : %s",
-				rec.Name, rec.MAC, err)
-			return -1, "", err
-		}
+		return &status, nil
 	}
 
 	// If we end up here that means that no record was found in the provisioning, so return
 	// a status of -1, w/o an error
-	return -1, "", nil
+	return nil, nil
 }
 
 func (c *Config) provision(rec AddressRec) error {
@@ -154,51 +172,33 @@
 		return nil
 	}
 
-	last, err := c.storage.LastProvisioned(rec.MAC)
-	if err != nil {
-		return err
-	}
-
-	if last == nil {
-		log.Debugf("no TTL for device '%s' (%s, %s)",
-			rec.Name, rec.IP, rec.MAC)
-	} else {
-		log.Debugf("TTL for device '%s' (%s, %s) is %v",
-			rec.Name, rec.IP, rec.MAC, *last)
-	}
-
 	// Verify if the provision status of the node is complete, if in an error state then TTL means
 	// nothing
-	state, message, err := c.getProvisionedState(rec)
-	switch state {
-	case 0, 1: // Pending or Running
-		log.Debugf("device '%s' (%s, %s) is being provisioned",
-			rec.Name, rec.IP, rec.MAC)
-		return nil
-	case 2: // Complete
-		log.Debugf("device '%s' (%s, %s) has completed provisioning",
-			rec.Name, rec.IP, rec.MAC)
-		// If no last record then set the TTL
-		if last == nil {
-			now := time.Now()
-			last = &now
-			c.storage.MarkProvisioned(rec.MAC, last)
-			log.Debugf("Storing TTL for device '%s' (%s, %s) as %v",
-				rec.Name, rec.IP, rec.MAC, now)
+	state, err := c.getProvisionedState(rec)
+	if state != nil {
+		switch state.Status {
+		case Pending, Running: // Pending or Running
+			log.Debugf("device '%s' (%s, %s) is being provisioned",
+				rec.Name, rec.IP, rec.MAC)
 			return nil
+		case Complete: // Complete
+			log.Debugf("device '%s' (%s, %s) has completed provisioning",
+				rec.Name, rec.IP, rec.MAC)
+		case Failed: // Failed
+			log.Debugf("device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
+				rec.Name, rec.IP, rec.MAC, state.Message)
+		default: // Unknown state
+			log.Debugf("device '%s' (%s, %s) has unknown provisioning state '%d', will provision",
+				rec.Name, rec.IP, rec.MAC, state.Status)
 		}
-	case 3: // Failed
-		log.Debugf("device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
-			rec.Name, rec.IP, rec.MAC, message)
-		c.storage.ClearProvisioned(rec.MAC)
-		last = nil
-	default: // No record
+	} else {
+		log.Debugf("device '%s' (%s, %s) has no provisioning record",
+			rec.Name, rec.IP, rec.MAC)
 	}
 
 	// If TTL is 0 then we will only provision a switch once.
-	if last == nil || (c.ttl > 0 && time.Since(*last) > c.ttl) {
-		if last != nil {
-			c.storage.ClearProvisioned(rec.MAC)
+	if state == nil || (c.ttl > 0 && time.Since(time.Unix(state.Timestamp, 0)) > c.ttl) {
+		if state != nil {
 			log.Debugf("device '%s' (%s, %s) TTL expired, reprovisioning",
 				rec.Name, rec.IP, rec.MAC)
 		}
@@ -243,9 +243,6 @@
 	config.vendors, err = NewVendors(config.VendorsURL)
 	checkError(err, "Unable to create known vendors list from specified URL '%s' : %s", config.VendorsURL, err)
 
-	config.storage, err = NewStorage(config.StorageURL)
-	checkError(err, "Unable to create require storage for specified URL '%s' : %s", config.StorageURL, err)
-
 	config.addressSource, err = NewAddressSource(config.AddressURL)
 	checkError(err, "Unable to create required address source for specified URL '%s' : %s", config.AddressURL, err)
 
@@ -257,7 +254,6 @@
 
 	log.Infof(`Configuration:
 		Vendors URL:       %s
-		Storage URL:       %s
 		Poll Interval:     %s
 		Address Source:    %s
 		Provision TTL:     %s
@@ -267,7 +263,7 @@
 		Script:            %s
 		Log Level:         %s
 		Log Format:        %s`,
-		config.VendorsURL, config.StorageURL, config.PollInterval, config.AddressURL, config.ProvisionTTL,
+		config.VendorsURL, config.PollInterval, config.AddressURL, config.ProvisionTTL,
 		config.ProvisionURL, config.RoleSelectorURL, config.DefaultRole, config.Script,
 		config.LogLevel, config.LogFormat)