blob: 9f38b2393141f4fad49671eb77d0eb7a9bd0fe96 [file] [log] [blame]
David K. Bainbridgeefa951d2016-05-26 10:54:25 -07001package main
2
3import (
4 "encoding/json"
5 "github.com/fzzy/radix/redis"
6 "log"
7 "net/url"
8 "os"
9)
10
11type ProvisionState int8
12
13const (
14 Unprovisioned ProvisionState = iota
15 ProvisionError
16 Provisioning
17 Provisioned
18)
19
20func (s *ProvisionState) String() string {
21 switch *s {
22 case Unprovisioned:
23 return "UNPROVISIONED"
24 case ProvisionError:
25 return "PROVISIONERROR"
26 case Provisioning:
27 return "PROVISIONING"
28 case Provisioned:
29 return "PROVISIONED"
30 default:
31 return "UNKNOWN"
32 }
33}
34
35// TrackerRecord state kept for each node to be provisioned
36type TrackerRecord struct {
37 State ProvisionState
38
39 // Timeestamp maintains the time the node started provisioning, eventually will be used to time out
40 // provisinion states
41 Timestamp int64
42}
43
44// Tracker used to track if a node has been post deployed provisioned
45type Tracker interface {
46 Get(key string) (*TrackerRecord, error)
47 Set(key string, record *TrackerRecord) error
48 Clear(key string) error
49}
50
51// RedisTracker redis implementation of the tracker interface
52type RedisTracker struct {
53 client *redis.Client
54}
55
56func (t *RedisTracker) Get(key string) (*TrackerRecord, error) {
57 reply := t.client.Cmd("get", key)
58 if reply.Err != nil {
59 return nil, reply.Err
60 }
61 if reply.Type == redis.NilReply {
62 var record TrackerRecord
63 record.State = Unprovisioned
64 return &record, nil
65 }
66
67 value, err := reply.Str()
68 if err != nil {
69 return nil, err
70 }
71 var record TrackerRecord
72 err = json.Unmarshal([]byte(value), &record)
73 if err != nil {
74 return nil, err
75 }
76 return &record, nil
77}
78
79func (t *RedisTracker) Set(key string, record *TrackerRecord) error {
80 reply := t.client.Cmd("set", key, true)
81 return reply.Err
82}
83
84func (t *RedisTracker) Clear(key string) error {
85 reply := t.client.Cmd("del", key)
86 return reply.Err
87}
88
89// MemoryTracker in memory implementation of the tracker interface
90type MemoryTracker struct {
91 data map[string]TrackerRecord
92}
93
94func (m *MemoryTracker) Get(key string) (*TrackerRecord, error) {
95 if value, ok := m.data[key]; ok {
96 return &value, nil
97 }
98 var record TrackerRecord
99 record.State = Unprovisioned
100 return &record, nil
101}
102
103func (m *MemoryTracker) Set(key string, record *TrackerRecord) error {
104 m.data[key] = *record
105 return nil
106}
107
108func (m *MemoryTracker) Clear(key string) error {
109 delete(m.data, key)
110 return nil
111}
112
113// NetTracker constructs an implemetation of the Tracker interface. Which implementation selected
114// depends on the environment. If a link to a redis instance is defined then this will
115// be used, else an in memory version will be used.
116func NewTracker() Tracker {
117 // Check the environment to see if we are linked to a redis DB
118 if os.Getenv("AUTODB_ENV_REDIS_VERSION") != "" {
119 tracker := new(RedisTracker)
120 if spec := os.Getenv("AUTODB_PORT"); spec != "" {
121 port, err := url.Parse(spec)
122 checkError(err, "[error] unable to lookup to redis database : %s", err)
123 tracker.client, err = redis.Dial(port.Scheme, port.Host)
124 checkError(err, "[error] unable to connect to redis database : '%s' : %s", port, err)
125 log.Println("[info] Using REDIS to track provisioning status of nodes")
126 return tracker
127 } else {
128 log.Fatalf("[error] looks like we are configured for REDIS, but no PORT defined in environment")
129 }
130 }
131
132 // Else fallback to an in memory tracker
133 tracker := new(MemoryTracker)
134 tracker.data = make(map[string]TrackerRecord)
135 log.Println("[info] Using memory based structures to track provisioning status of nodes")
136 return tracker
137}