|
1 | 1 | package metrics |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
| 5 | + "errors" |
4 | 6 | "fmt" |
5 | 7 | "net/http" |
6 | 8 | "time" |
7 | 9 |
|
8 | 10 | "github.com/prometheus/client_golang/prometheus/promhttp" |
9 | 11 | "github.com/spf13/cobra" |
| 12 | + "k8s.io/klog" |
10 | 13 | ) |
11 | 14 |
|
12 | 15 | type Server struct { |
13 | | - server httpBgServer |
14 | | - Watcher Watcher |
| 16 | + http http.Server |
| 17 | + shutdownFinished chan struct{} |
15 | 18 | } |
16 | 19 |
|
17 | 20 | func (server *Server) ListenAndServe() { |
18 | | - server.server.ListenAndServe() |
| 21 | + err := server.http.ListenAndServe() |
| 22 | + |
| 23 | + if errors.Is(err, http.ErrServerClosed) { |
| 24 | + // Expected in case of shutdown. |
| 25 | + err = nil |
| 26 | + } else { |
| 27 | + klog.Warning("HTTP server failed: " + err.Error()) |
| 28 | + } |
| 29 | + |
| 30 | + <-server.shutdownFinished |
19 | 31 | } |
20 | 32 |
|
21 | | -func (server *Server) Shutdown() { |
22 | | - if server.Watcher.tickerStop != nil { |
23 | | - server.Watcher.Stop() |
| 33 | +func (server *Server) shutdown(waitForExit context.Context, exitTimeout time.Duration) { |
| 34 | + <-waitForExit.Done() |
| 35 | + |
| 36 | + ctx, cancel := context.WithTimeout(context.Background(), exitTimeout) |
| 37 | + defer cancel() |
| 38 | + |
| 39 | + err := server.http.Shutdown(ctx) |
| 40 | + if err != nil { |
| 41 | + klog.Warning("Error while shutting down HTTP server: " + err.Error()) |
| 42 | + } else { |
| 43 | + klog.Info("HTTP server successfully shut down.") |
| 44 | + } |
| 45 | + close(server.shutdownFinished) |
| 46 | +} |
| 47 | + |
| 48 | +type Observable func() |
| 49 | + |
| 50 | +func poll(ctx context.Context, period time.Duration, observe Observable) { |
| 51 | + ticker := time.NewTicker(period) |
| 52 | + defer ticker.Stop() |
| 53 | + |
| 54 | + for { |
| 55 | + select { |
| 56 | + case <-ctx.Done(): |
| 57 | + return |
| 58 | + case <-ticker.C: |
| 59 | + observe() |
| 60 | + } |
24 | 61 | } |
25 | | - server.server.Shutdown() |
26 | 62 | } |
27 | 63 |
|
28 | 64 | type ServerConfig struct { |
29 | 65 | host string |
30 | 66 | port int16 |
31 | 67 | pathPrefix string |
32 | | - enable bool |
| 68 | + Enable bool |
33 | 69 | } |
34 | 70 |
|
35 | 71 | func (config *ServerConfig) CommandLineParameters(root *cobra.Command) { |
36 | 72 | root.PersistentFlags().StringVar(&config.host, "metrics-host", "localhost", "Host name or ip address for Prometheus metrics") |
37 | 73 | root.PersistentFlags().Int16Var(&config.port, "metrics-port", 80, "Port for Prometheus metrics") |
38 | | - root.PersistentFlags().BoolVar(&config.enable, "metrics-enable", false, "Enable Prometheus metrics") |
| 74 | + root.PersistentFlags().BoolVar(&config.Enable, "metrics-enable", false, "Enable Prometheus metrics") |
39 | 75 |
|
40 | 76 | config.pathPrefix = "/metrics" |
41 | 77 | } |
42 | 78 |
|
43 | | -func (config *ServerConfig) NewServer(meters *[]Observable, pollDelay, shutdownTimeout time.Duration) *Server { |
| 79 | +func (config *ServerConfig) NewServer(ctx context.Context, shutdownTimeout, pollPeriod time.Duration, meters *[]Observable) *Server { |
44 | 80 | mux := http.NewServeMux() |
45 | 81 | mux.Handle(config.pathPrefix, promhttp.Handler()) |
46 | 82 |
|
47 | 83 | server := Server{ |
48 | | - server: httpBgServer{ |
49 | | - server: http.Server{Addr: fmt.Sprintf("%s:%d", config.host, config.port), Handler: mux}, |
50 | | - shutdownFinished: make(chan struct{}), |
51 | | - shutdownTimeout: shutdownTimeout, |
52 | | - }, |
| 84 | + http: http.Server{Addr: fmt.Sprintf("%s:%d", config.host, config.port), Handler: mux}, |
| 85 | + shutdownFinished: make(chan struct{}), |
53 | 86 | } |
54 | 87 |
|
55 | | - server.Watcher.Poll( |
| 88 | + go poll(ctx, pollPeriod, |
56 | 89 | func() { |
57 | 90 | for _, observer := range *meters { |
58 | 91 | observer() |
59 | 92 | } |
60 | 93 | }, |
61 | | - pollDelay, |
62 | 94 | ) |
63 | 95 |
|
| 96 | + go server.shutdown(ctx, shutdownTimeout) |
| 97 | + |
64 | 98 | return &server |
65 | 99 | } |
66 | | - |
67 | | -func (config *ServerConfig) IsEnabled() bool { return config.enable } |
|
0 commit comments