refactor as procedural
[arvados.git] / services / boot / booter.go
1 package main
2
3 import (
4         "context"
5         "fmt"
6         "sync"
7 )
8
9 // A Booter ensures some piece of the system ("target") is correctly
10 // installed, configured, running, or working.
11 type Booter interface {
12         // Inspect, repair, and report the current state of the target.
13         Boot(context.Context) error
14 }
15
16 var cfgKey = &struct{}{}
17
18 func cfg(ctx context.Context) *Config {
19         return ctx.Value(cfgKey).(*Config)
20 }
21
22 func withCfg(ctx context.Context, cfg *Config) context.Context {
23         return context.WithValue(ctx, cfgKey, cfg)
24 }
25
26 type Series []Booter
27
28 func (sb Series) Boot(ctx context.Context) error {
29         for _, b := range sb {
30                 err := b.Boot(ctx)
31                 if err != nil {
32                         return err
33                 }
34         }
35         return nil
36 }
37
38 type Concurrent []Booter
39
40 func (cb Concurrent) Boot(ctx context.Context) error {
41         errs := make([]error, len(cb))
42         var wg sync.WaitGroup
43         wg.Add(len(cb))
44         for i, b := range cb {
45                 i, b := i, b
46                 go func() {
47                         defer wg.Done()
48                         errs[i] = b.Boot(ctx)
49                 }()
50         }
51         wg.Wait()
52         return NewMultipleError(errs)
53 }
54
55 type MultipleError struct {
56         error
57         errors []error
58 }
59
60 func NewMultipleError(errs []error) error {
61         var errors []error
62         for _, err := range errs {
63                 switch err := err.(type) {
64                 case *MultipleError:
65                         errors = append(errors, err.errors...)
66                 case nil:
67                 default:
68                         errors = append(errors, err)
69                 }
70         }
71         if len(errors) == 0 {
72                 return nil
73         }
74         if len(errors) == 1 {
75                 return errors[0]
76         }
77         return &MultipleError{
78                 error:  fmt.Errorf("%d errors", len(errors)),
79                 errors: errors,
80         }
81 }