blob: 9f38b2393141f4fad49671eb77d0eb7a9bd0fe96 [file] [log] [blame]
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
}