fade out when stale
[arvados.git] / services / boot / task.go
1 package main
2
3 import (
4         "context"
5         "log"
6         "sync"
7         "time"
8 )
9
10 type taskState string
11
12 const (
13         StateUnchecked taskState = "Unchecked"
14         StateChecking            = "Checking"
15         StateFixing              = "Fixing"
16         StateFailed              = "Failed"
17         StateOK                  = "OK"
18 )
19
20 type version int64
21
22 type taskStateMap struct {
23         s       map[task]taskState
24         cond    *sync.Cond
25         version version
26 }
27
28 var TaskState = taskStateMap{
29         s:    make(map[task]taskState),
30         cond: sync.NewCond(&sync.Mutex{}),
31 }
32
33 func (m *taskStateMap) Set(t task, s taskState) {
34         m.cond.L.Lock()
35         defer m.cond.L.Unlock()
36         if old, ok := m.s[t]; ok && old == s {
37                 return
38         }
39         m.s[t] = s
40         m.version++
41         m.cond.Broadcast()
42 }
43
44 func (m *taskStateMap) Version() version {
45         m.cond.L.Lock()
46         defer m.cond.L.Unlock()
47         return m.version
48 }
49
50 func (m *taskStateMap) Get(t task) taskState {
51         m.cond.L.Lock()
52         defer m.cond.L.Unlock()
53         if s, ok := m.s[t]; ok {
54                 return s
55         } else {
56                 return StateUnchecked
57         }
58 }
59
60 type repEnt struct {
61         ShortName   string
62         Description string
63         State       taskState
64         Children    []repEnt
65 }
66
67 func (m *taskStateMap) Wait(v version, t time.Duration, ctx context.Context) bool {
68         ready := make(chan struct{})
69         var done bool
70         go func() {
71                 m.cond.L.Lock()
72                 defer m.cond.L.Unlock()
73                 for v == m.version && !done {
74                         m.cond.Wait()
75                 }
76                 close(ready)
77         }()
78         select {
79         case <-ready:
80                 return true
81         case <-ctx.Done():
82         case <-time.After(t):
83         }
84         done = true
85         m.cond.Broadcast()
86         return false
87 }
88
89 func report(tasks []task) ([]repEnt, version) {
90         v := TaskState.Version()
91         if len(tasks) == 0 {
92                 return nil, v
93         }
94         var rep []repEnt
95         for _, t := range tasks {
96                 crep, _ := report(t.Children())
97                 rep = append(rep, repEnt{
98                         ShortName:   t.ShortName(),
99                         Description: t.String(),
100                         State:       TaskState.Get(t),
101                         Children:    crep,
102                 })
103         }
104         return rep, v
105 }
106
107 func runTasks(cfg *Config, tasks []task) {
108         for _, t := range tasks {
109                 t.Init(cfg)
110         }
111         for _, t := range tasks {
112                 if TaskState.Get(t) == taskState("") {
113                         TaskState.Set(t, StateChecking)
114                 }
115                 err := t.Check()
116                 if err == nil {
117                         log.Printf("%s: OK", t)
118                         TaskState.Set(t, StateOK)
119                         continue
120                 }
121                 log.Printf("%s: %s", t, err)
122                 if !t.CanFix() {
123                         log.Printf("%s: can't fix")
124                         TaskState.Set(t, StateFailed)
125                         continue
126                 }
127                 TaskState.Set(t, StateFixing)
128                 if err = t.Fix(); err != nil {
129                         log.Printf("%s: can't fix: %s", t, err)
130                         TaskState.Set(t, StateFailed)
131                         continue
132                 }
133                 if err = t.Check(); err != nil {
134                         log.Printf("%s: fixed, but still broken?!: %s", t, err)
135                         TaskState.Set(t, StateFailed)
136                         continue
137                 }
138                 log.Printf("%s: OK", t)
139                 TaskState.Set(t, StateOK)
140         }
141 }
142
143 type task interface {
144         Init(*Config)
145         ShortName() string
146         String() string
147         Check() error
148         CanFix() bool
149         Fix() error
150         Children() []task
151 }