-
Notifications
You must be signed in to change notification settings - Fork 0
/
init.go
98 lines (84 loc) · 2.17 KB
/
init.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package horizon
import (
"gitlab.com/distributed_lab/logan/v3"
"gitlab.com/distributed_lab/logan/v3/errors"
"gitlab.com/tokend/horizon/log"
)
// InitFn is a function that contributes to the initialization of an App struct
type InitFn func(*App)
type initializer struct {
Name string
Fn InitFn
Deps []string
}
type initializerSet []initializer
var appInit initializerSet
// Add adds a new initializer into the chain
func (is *initializerSet) Add(name string, fn InitFn, deps ...string) {
*is = append(*is, initializer{
Name: name,
Fn: fn,
Deps: deps,
})
}
// Run initializes the provided application, but running every Initializer
func (is *initializerSet) Run(app *App) {
err := is.checkDuplicates()
if err != nil {
log.WithField("err", err).Fatal("failed to init initializer")
}
init := *is
alreadyRun := make(map[string]bool)
for {
ranInitializer := false
for _, i := range init {
// if we've already been run, skip
if ok := alreadyRun[i.Name]; ok {
continue
}
// if any of our dependencies haven't been run, skip
isReadyToRun := true
for _, d := range i.Deps {
if ok := alreadyRun[d]; !ok {
alreadyRun[d] = false
isReadyToRun = false
break
}
}
if !isReadyToRun {
alreadyRun[i.Name] = false
continue
}
log.WithField("init_name", i.Name).Debug("running initializer")
i.Fn(app)
alreadyRun[i.Name] = true
ranInitializer = true
}
// If, after a full loop through the initializers we ran nothing
// we are done
if !ranInitializer {
break
}
}
// if we didn't get to run all initializers, we have a cycle
if len(alreadyRun) != len(init) {
var failedToRun []string
for name, isStarted := range alreadyRun {
if !isStarted {
failedToRun = append(failedToRun, name)
}
}
log.WithField("failedToRun", failedToRun).Panic("initializer cycle detected")
}
}
func (is *initializerSet) checkDuplicates() error {
init := *is
unique := map[string]struct{}{}
for _, runner := range init {
if _, exists := unique[runner.Name]; exists {
return errors.From(errors.New("duplicated initializer"), logan.F{"name": runner.Name})
}
unique[runner.Name] = struct{}{}
}
return nil
}