-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathservices.go
More file actions
176 lines (149 loc) · 5.17 KB
/
services.go
File metadata and controls
176 lines (149 loc) · 5.17 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package fiber
import (
"context"
"errors"
"fmt"
"io"
utilsstrings "github.com/gofiber/utils/v2/strings"
)
// Service is an interface that defines the methods for a service.
type Service interface {
// Start starts the service, returning an error if it fails.
Start(ctx context.Context) error
// String returns a string representation of the service.
// It is used to print a human-readable name of the service in the startup message.
String() string
// State returns the current state of the service.
State(ctx context.Context) (string, error)
// Terminate terminates the service, returning an error if it fails.
Terminate(ctx context.Context) error
}
// hasConfiguredServices Checks if there are any services for the current application.
func (app *App) hasConfiguredServices() bool {
return len(app.configured.Services) > 0
}
func (app *App) validateConfiguredServices() error {
return validateServicesSlice(app.configured.Services)
}
func validateServicesSlice(services []Service) error {
for idx, srv := range services {
if srv == nil {
return fmt.Errorf("fiber: service at index %d is nil", idx)
}
}
return nil
}
// initServices If the app is configured to use services, this function registers
// a post shutdown hook to shutdown them after the server is closed.
// This function panics if there is an error starting the services.
func (app *App) initServices() {
if !app.hasConfiguredServices() {
return
}
if err := app.startServices(app.servicesStartupCtx()); err != nil {
panic(err)
}
}
// servicesStartupCtx Returns the context for the services startup.
// If the ServicesStartupContextProvider is not set, it returns a new background context.
func (app *App) servicesStartupCtx() context.Context {
if app.configured.ServicesStartupContextProvider != nil {
return app.configured.ServicesStartupContextProvider()
}
return context.Background()
}
// servicesShutdownCtx Returns the context for the services shutdown.
// If the ServicesShutdownContextProvider is not set, it returns a new background context.
func (app *App) servicesShutdownCtx() context.Context {
if app.configured.ServicesShutdownContextProvider != nil {
return app.configured.ServicesShutdownContextProvider()
}
return context.Background()
}
// startServices Handles the start process of services for the current application.
// Iterates over all configured services and tries to start them, returning an error if any error occurs.
func (app *App) startServices(ctx context.Context) error {
if !app.hasConfiguredServices() {
return nil
}
var errs []error
for idx, srv := range app.configured.Services {
if srv == nil {
return fmt.Errorf("fiber: service at index %d is nil", idx)
}
if err := ctx.Err(); err != nil {
// Context is canceled, return an error the soonest possible, so that
// the user can see the context cancellation error and act on it.
return fmt.Errorf("context canceled while starting service %s: %w", srv.String(), err)
}
err := srv.Start(ctx)
if err == nil {
// mark the service as started
app.state.setService(srv)
continue
}
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("service %s start: %w", srv.String(), err)
}
errs = append(errs, fmt.Errorf("service %s start: %w", srv.String(), err))
}
return errors.Join(errs...)
}
// shutdownServices Handles the shutdown process of services for the current application.
// Iterates over all the started services in reverse order and tries to terminate them,
// returning an error if any error occurs.
func (app *App) shutdownServices(ctx context.Context) error {
if app.state.ServicesLen() == 0 {
return nil
}
var errs []error
for key, srv := range app.state.Services() {
if srv == nil {
return fmt.Errorf("fiber: service %q is nil", key)
}
if err := ctx.Err(); err != nil {
// Context is canceled, do a best effort to terminate the services.
errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
continue
}
err := srv.Terminate(ctx)
if err != nil {
// Best effort to terminate the services.
errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
continue
}
// Remove the service from the State
app.state.deleteService(srv)
}
return errors.Join(errs...)
}
// logServices logs information about services and returns an error
// if any configured service is nil.
func (app *App) logServices(ctx context.Context, out io.Writer, colors *Colors) error {
if !app.hasConfiguredServices() {
return nil
}
scheme := colors
if scheme == nil {
scheme = &DefaultColors
}
fmt.Fprintf(out,
"%sINFO%s Services: \t%s%d%s\n",
scheme.Green, scheme.Reset, scheme.Blue, app.state.ServicesLen(), scheme.Reset)
for key, srv := range app.state.Services() {
if srv == nil {
return fmt.Errorf("fiber: service %q is nil", key)
}
var state string
var stateColor string
state, err := srv.State(ctx)
if err != nil {
state = errString
stateColor = scheme.Red
} else {
stateColor = scheme.Blue
}
fmt.Fprintf(out, "%sINFO%s 🧩 %s[ %s ] %s%s\n", scheme.Green, scheme.Reset, stateColor, utilsstrings.ToUpper(state), srv.String(), scheme.Reset)
}
return nil
}