-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
server.go
144 lines (116 loc) · 3.1 KB
/
server.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package serverv2
import (
"context"
"fmt"
"os"
"github.com/pelletier/go-toml/v2"
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
"cosmossdk.io/log"
)
// ServerModule is a server module that can be started and stopped.
type ServerModule interface {
Name() string
Start(context.Context) error
Stop(context.Context) error
}
// HasCLICommands is a server module that has CLI commands.
type HasCLICommands interface {
CLICommands() CLIConfig
}
// HasConfig is a server module that has a config.
type HasConfig interface {
Config() any
}
var _ ServerModule = (*Server)(nil)
// Configs returns a viper instance of the config file
func ReadConfig(configPath string) (*viper.Viper, error) {
v := viper.New()
v.SetConfigFile(configPath)
v.SetConfigType("toml")
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config: %s: %w", configPath, err)
}
v.WatchConfig()
return v, nil
}
type Server struct {
logger log.Logger
modules []ServerModule
}
func NewServer(logger log.Logger, modules ...ServerModule) *Server {
return &Server{
logger: logger,
modules: modules,
}
}
func (s *Server) Name() string {
return "server"
}
// Start starts all modules concurrently.
func (s *Server) Start(ctx context.Context) error {
s.logger.Info("starting servers...")
g, ctx := errgroup.WithContext(ctx)
for _, mod := range s.modules {
mod := mod
g.Go(func() error {
return mod.Start(ctx)
})
}
if err := g.Wait(); err != nil {
return fmt.Errorf("failed to start servers: %w", err)
}
serverCfg := ctx.Value(ServerContextKey).(Config)
if serverCfg.StartBlock {
<-ctx.Done()
}
return nil
}
// Stop stops all modules concurrently.
func (s *Server) Stop(ctx context.Context) error {
s.logger.Info("stopping servers...")
g, ctx := errgroup.WithContext(ctx)
for _, mod := range s.modules {
mod := mod
g.Go(func() error {
return mod.Stop(ctx)
})
}
return g.Wait()
}
// CLICommands returns all CLI commands of all modules.
func (s *Server) CLICommands() CLIConfig {
commands := CLIConfig{}
for _, mod := range s.modules {
if climod, ok := mod.(HasCLICommands); ok {
commands.Commands = append(commands.Commands, climod.CLICommands().Commands...)
commands.Queries = append(commands.Queries, climod.CLICommands().Queries...)
commands.Txs = append(commands.Txs, climod.CLICommands().Txs...)
}
}
return commands
}
// Configs returns all configs of all server modules.
func (s *Server) Configs() map[string]any {
cfgs := make(map[string]any)
for _, mod := range s.modules {
if configmod, ok := mod.(HasConfig); ok {
cfg := configmod.Config()
cfgs[mod.Name()] = cfg
}
}
return cfgs
}
// WriteConfig writes the config to the given path.
// Note: it does not use viper.WriteConfigAs because we do not want to store flag values in the config.
func (s *Server) WriteConfig(configPath string) error {
cfgs := s.Configs()
b, err := toml.Marshal(cfgs)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(configPath, b, 0o600); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
return nil
}