Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Commit 9016ca4

Browse files
author
Audrius Karabanovas
authored
Merge pull request #20 from trustpilot/windows-service
Windows service
2 parents a87e438 + 03856cf commit 9016ca4

File tree

8 files changed

+144
-14
lines changed

8 files changed

+144
-14
lines changed

.github/main.workflow

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ workflow "build-and-deploy" {
55

66
action "build" {
77
uses="cedrickring/golang-action/[email protected]"
8+
args="make crossbuild"
89
}

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
vendor
22
.build
33
.tarballs
4-
beat-exporter
4+
.DS_Store
5+
beat-exporter
6+
beat-exporter.exe

Gopkg.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+4
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@
99
[[constraint]]
1010
name = "github.com/sirupsen/logrus"
1111
version = "1.4.2"
12+
13+
[[constraint]]
14+
branch = "master"
15+
name = "golang.org/x/sys"

Makefile

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ build: promu
4949
@echo ">> building binaries"
5050
@$(PROMU) build --prefix $(PREFIX)
5151

52-
crossbuild: promu
52+
crossbuild: dependencies format vet test
5353
@echo ">> cross-building binaries"
54-
@$(PROMU) crossbuild
54+
@$(GO) get github.com/mitchellh/gox
55+
@gox -arch="386 amd64" -os="linux darwin windows" --output=".build/{{.OS}}-{{.Arch}}/{{.Dir}}"
5556

5657
tarball: promu
5758
@echo ">> building release tarball"

internal/service/handler.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// +build linux darwin
2+
3+
package service
4+
5+
import (
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
// SetupServiceListener setups singal handler
14+
func SetupServiceListener(stopCh chan<- bool, serviceName string, logger log.StdLogger) error {
15+
go func() {
16+
sigs := make(chan os.Signal, 1)
17+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGHUP)
18+
logger.Printf("Signal received: %v", <-sigs)
19+
stopCh <- true
20+
close(stopCh)
21+
}()
22+
23+
return nil
24+
}

internal/service/handler_windows.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// +build windows
2+
3+
package service
4+
5+
import (
6+
"fmt"
7+
log "github.com/sirupsen/logrus"
8+
"golang.org/x/sys/windows/svc"
9+
)
10+
11+
type beatExporterService struct {
12+
stopCh chan<- bool
13+
}
14+
15+
func (s *beatExporterService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
16+
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
17+
changes <- svc.Status{State: svc.StartPending}
18+
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
19+
loop:
20+
for {
21+
select {
22+
case c := <-r:
23+
switch c.Cmd {
24+
case svc.Interrogate:
25+
changes <- c.CurrentStatus
26+
case svc.Stop, svc.Shutdown:
27+
s.stopCh <- true
28+
break loop
29+
default:
30+
log.Error(fmt.Sprintf("unexpected control request #%d", c))
31+
}
32+
}
33+
}
34+
changes <- svc.Status{State: svc.StopPending}
35+
return
36+
}
37+
38+
// SetupServiceListener setups service handler for windows
39+
func SetupServiceListener(stopCh chan<- bool, serviceName string, logger log.StdLogger) error {
40+
isInteractive, err := svc.IsAnInteractiveSession()
41+
if err != nil {
42+
return err
43+
}
44+
45+
if !isInteractive {
46+
go func() {
47+
err = svc.Run(serviceName, &beatExporterService{stopCh: stopCh})
48+
if err != nil {
49+
logger.Printf("Failed to start service: %v", err)
50+
}
51+
}()
52+
}
53+
54+
return nil
55+
}

main.go

+52-11
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ import (
1717
"github.com/prometheus/client_golang/prometheus/promhttp"
1818
"github.com/prometheus/common/version"
1919
"github.com/trustpilot/beat-exporter/collector"
20+
"github.com/trustpilot/beat-exporter/internal/service"
21+
)
22+
23+
const (
24+
serviceName = "beat_exporter"
2025
)
2126

2227
func main() {
2328
var (
24-
Name = "beat_exporter"
29+
Name = serviceName
2530
listenAddress = flag.String("web.listen-address", ":9479", "Address to listen on for web interface and telemetry.")
2631
metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
2732
beatURI = flag.String("beat.uri", "http://localhost:5066", "HTTP API address of beat.")
@@ -57,16 +62,36 @@ func main() {
5762

5863
var beatInfo *collector.BeatInfo
5964

65+
stopCh := make(chan bool)
66+
67+
err = service.SetupServiceListener(stopCh, serviceName, log.StandardLogger())
68+
if err != nil {
69+
log.WithFields(log.Fields{
70+
"err": err,
71+
}).Errorf("could not setup service listener: %v", err)
72+
}
73+
74+
t := time.NewTicker(1 * time.Second)
75+
76+
beatdiscovery:
6077
for {
61-
beatInfo, err = loadBeatType(httpClient, *beatURL)
62-
if err != nil {
63-
log.Errorf("Could not load beat type, with error: %v, retrying in 5s", err)
64-
time.Sleep(5 * time.Second)
65-
} else {
66-
break
78+
select {
79+
case <-t.C:
80+
beatInfo, err = loadBeatType(httpClient, *beatURL)
81+
if err != nil {
82+
log.Errorf("Could not load beat type, with error: %v, retrying in 1s", err)
83+
continue
84+
}
85+
86+
break beatdiscovery
87+
88+
case <-stopCh:
89+
os.Exit(0) // signal received, stop gracefully
6790
}
6891
}
6992

93+
t.Stop()
94+
7095
// version metric
7196
registry := prometheus.NewRegistry()
7297
versionMetric := version.NewCollector(Name)
@@ -88,17 +113,33 @@ func main() {
88113
"addr": *listenAddress,
89114
}).Infof("Starting exporter with configured type: %s", beatInfo.Beat)
90115

91-
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
116+
go func() {
117+
defer func() {
118+
stopCh <- true
119+
}()
92120

93-
log.WithFields(log.Fields{
94-
"err": err,
95-
}).Errorf("http server quit with error: %v", err)
121+
log.Info("Starting listener")
122+
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
123+
124+
log.WithFields(log.Fields{
125+
"err": err,
126+
}).Errorf("http server quit with error: %v", err)
127+
128+
}
129+
log.Info("Listener exited")
130+
}()
96131

132+
for {
133+
if <-stopCh {
134+
log.Info("Shutting down beats exporter")
135+
break
136+
}
97137
}
98138
}
99139

100140
// IndexHandler returns a http handler with the correct metricsPath
101141
func IndexHandler(metricsPath string) http.HandlerFunc {
142+
102143
indexHTML := `
103144
<html>
104145
<head>

0 commit comments

Comments
 (0)