-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathmain.go
104 lines (81 loc) · 2.72 KB
/
main.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
// This file is part of conntrack-stats-exporter.
//
// conntrack-stats-exporter is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// conntrack-stats-exporter is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with conntrack-stats-exporter. If not, see
// <http://www.gnu.org/licenses/>.
package main
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"runtime"
"runtime/debug"
"sync"
"syscall"
"github.com/jwkohnen/conntrack-stats-exporter/exporter"
)
func main() {
// Set Go max procs to 1 even if number of (logical) CPUs is > 1. This is a low performance program that might run
// in an environment with very limited CPU resources via cgroups (e.g. Kubernetes resource limit). GOMAXPROCS of 1
// prevents the Go scheduler from using too much scheduler overhead in such environments.
//
// Usually I'd use go.uber.org/automaxprocs/maxprocs, but hard coding 1 is a better solution than having another
// dependency.
_ = runtime.GOMAXPROCS(1)
if os.Getenv("GOGC") == "" {
// Reduce memory overhead. This is a low performance program;
// the CPU penalty is negligible.
debug.SetGCPercent(10)
}
cfg, opts := configure()
mux := http.NewServeMux()
mux.Handle(cfg.path, newAbortHandler(exporter.Handler(opts...)))
srv := &http.Server{
Addr: cfg.addr,
Handler: mux,
ReadTimeout: cfg.timeoutHTTP,
WriteTimeout: cfg.timeoutHTTP,
}
shutdown := make(chan os.Signal, 1)
var (
receivedSignal os.Signal
wg sync.WaitGroup
)
wg.Add(1)
go func() {
defer wg.Done()
// Sadly Kubernetes sends SIGTERM, not SIGINT. CTRL+C on a TTY sends SIGINT.
signal.Notify(shutdown, os.Interrupt)
signal.Notify(shutdown, syscall.SIGTERM)
receivedSignal = <-shutdown
signal.Stop(shutdown)
shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.timeoutShutdown)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
abort(fmt.Errorf("error shutting down server: %w", err))
}
}()
cfg.logf("listening on %s with endpoint %q\n", cfg.addr, cfg.path)
err := srv.ListenAndServe()
wg.Wait()
if errors.Is(err, http.ErrServerClosed) {
const signaledExitCodeBase = 128
os.Exit(signaledExitCodeBase + int(receivedSignal.(syscall.Signal)))
}
if err != nil {
abort(err)
}
}