dev privileges, db
[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                         err := b.Boot(ctx)
49                         switch err.(type) {
50                         case nil:
51                         case *MultipleError:
52                         default:
53                                 err = fmt.Errorf("%T: %s", b, err)
54                         }
55                         errs[i] = err
56                 }()
57         }
58         wg.Wait()
59         return NewMultipleError(errs)
60 }
61
62 type MultipleError struct {
63         error
64         errors []error
65 }
66
67 func NewMultipleError(errs []error) error {
68         var errors []error
69         for _, err := range errs {
70                 switch err := err.(type) {
71                 case *MultipleError:
72                         errors = append(errors, err.errors...)
73                 case nil:
74                 default:
75                         errors = append(errors, err)
76                 }
77         }
78         if len(errors) == 0 {
79                 return nil
80         }
81         if len(errors) == 1 {
82                 return errors[0]
83         }
84         return &MultipleError{
85                 error:  fmt.Errorf("%d errors %q", len(errors), errors),
86                 errors: errors,
87         }
88 }