Skip to content

Commit 98827f6

Browse files
authored
MEDIUM: Add /health and /ready endpoint to stats server (#69)
In some setup, we'd like to wait until connect is ready before running the app. This PR adds a /ready endpoint that blocks until haproxy is setup. A /health endpoint is also added to monitor that consul connect is running.
1 parent 19573c8 commit 98827f6

File tree

5 files changed

+173
-103
lines changed

5 files changed

+173
-103
lines changed

haproxy/config.go

+7-10
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,7 @@ import (
1414
log "github.com/sirupsen/logrus"
1515
)
1616

17-
const (
18-
dataplaneUser = "haproxy"
19-
)
20-
21-
var dataplanePass string
22-
23-
var baseCfgTmpl = `
17+
const baseCfgTmpl = `
2418
global
2519
master-worker
2620
stats socket {{.SocketPath}} mode 600 level admin expose-fd listeners
@@ -71,6 +65,8 @@ type haConfig struct {
7165
StatsSock string
7266
DataplaneSock string
7367
DataplaneTransactionDir string
68+
DataplaneUser string
69+
DataplanePass string
7470
LogsSock string
7571
}
7672

@@ -116,14 +112,15 @@ func newHaConfig(baseDir string, sd *lib.Shutdown) (*haConfig, error) {
116112
}
117113
}()
118114

119-
dataplanePass = createRandomString()
115+
cfg.DataplanePass = createRandomString()
116+
cfg.DataplaneUser = "hapeoxy"
120117

121118
err = tmpl.Execute(cfgFile, baseParams{
122119
NbThread: runtime.GOMAXPROCS(0),
123120
SocketPath: cfg.StatsSock,
124121
LogsPath: cfg.LogsSock,
125-
DataplaneUser: dataplaneUser,
126-
DataplanePass: dataplanePass,
122+
DataplaneUser: cfg.DataplaneUser,
123+
DataplanePass: cfg.DataplanePass,
127124
})
128125
if err != nil {
129126
sd.Done()

haproxy/haproxy.go

+24-58
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ package haproxy
33
import (
44
"fmt"
55
"net"
6-
"net/http"
7-
"strconv"
8-
"time"
96

107
spoe "github.com/criteo/haproxy-spoe-go"
118
"github.com/haproxytech/haproxy-consul-connect/consul"
129
"github.com/haproxytech/haproxy-consul-connect/haproxy/dataplane"
1310
"github.com/haproxytech/haproxy-consul-connect/haproxy/haproxy_cmd"
1411
"github.com/haproxytech/haproxy-consul-connect/haproxy/state"
12+
"github.com/haproxytech/haproxy-consul-connect/haproxy/stats"
1513
"github.com/haproxytech/haproxy-consul-connect/lib"
1614
"github.com/hashicorp/consul/api"
17-
"github.com/prometheus/client_golang/prometheus/promhttp"
1815
log "github.com/sirupsen/logrus"
1916
"gopkg.in/mcuadros/go-syslog.v2"
2017
)
@@ -50,16 +47,16 @@ func New(consulClient *api.Client, cfg chan consul.Config, opts Options) *HAProx
5047
}
5148

5249
func (h *HAProxy) Run(sd *lib.Shutdown) error {
53-
return h.watch(sd)
54-
}
55-
56-
func (h *HAProxy) start(sd *lib.Shutdown) error {
5750
hc, err := newHaConfig(h.opts.ConfigBaseDir, sd)
5851
if err != nil {
5952
return err
6053
}
6154
h.haConfig = hc
6255

56+
return h.watch(sd)
57+
}
58+
59+
func (h *HAProxy) start(sd *lib.Shutdown) error {
6360
if h.opts.LogRequests {
6461
err := h.startLogger()
6562
if err != nil {
@@ -74,19 +71,19 @@ func (h *HAProxy) start(sd *lib.Shutdown) error {
7471
}
7572
}
7673

77-
dpc, err := haproxy_cmd.Start(sd, haproxy_cmd.Config{
74+
var err error
75+
h.dataplaneClient, err = haproxy_cmd.Start(sd, haproxy_cmd.Config{
7876
HAProxyPath: h.opts.HAProxyBin,
79-
HAProxyConfigPath: hc.HAProxy,
77+
HAProxyConfigPath: h.haConfig.HAProxy,
8078
DataplanePath: h.opts.DataplaneBin,
81-
DataplaneTransactionDir: hc.DataplaneTransactionDir,
82-
DataplaneSock: hc.DataplaneSock,
83-
DataplaneUser: dataplaneUser,
84-
DataplanePass: dataplanePass,
79+
DataplaneTransactionDir: h.haConfig.DataplaneTransactionDir,
80+
DataplaneSock: h.haConfig.DataplaneSock,
81+
DataplaneUser: h.haConfig.DataplaneUser,
82+
DataplanePass: h.haConfig.DataplanePass,
8583
})
8684
if err != nil {
8785
return err
8886
}
89-
h.dataplaneClient = dpc
9087

9188
err = h.startStats()
9289
if err != nil {
@@ -145,53 +142,22 @@ func (h *HAProxy) startStats() error {
145142
if h.opts.StatsListenAddr == "" {
146143
return nil
147144
}
148-
go func() {
149-
if !h.opts.StatsRegisterService {
150-
return
151-
}
152-
153-
_, portStr, err := net.SplitHostPort(h.opts.StatsListenAddr)
154-
if err != nil {
155-
log.Errorf("cannot parse stats listen addr: %s", err)
156-
}
157-
port, _ := strconv.Atoi(portStr)
158-
159-
reg := func() {
160-
err = h.consulClient.Agent().ServiceRegister(&api.AgentServiceRegistration{
161-
ID: fmt.Sprintf("%s-connect-stats", h.currentConsulConfig.ServiceID),
162-
Name: fmt.Sprintf("%s-connect-stats", h.currentConsulConfig.ServiceName),
163-
Port: port,
164-
Checks: api.AgentServiceChecks{
165-
&api.AgentServiceCheck{
166-
HTTP: fmt.Sprintf("http://localhost:%d/metrics", port),
167-
Interval: (10 * time.Second).String(),
168-
DeregisterCriticalServiceAfter: time.Minute.String(),
169-
},
170-
},
171-
Tags: []string{"connect-stats"},
172-
})
173-
if err != nil {
174-
log.Errorf("cannot register stats service: %s", err)
175-
}
176-
}
177145

178-
reg()
146+
s := stats.New(
147+
h.consulClient,
148+
h.dataplaneClient,
149+
h.Ready,
150+
stats.Config{
151+
RegisterService: h.opts.StatsRegisterService,
152+
ListenAddr: h.opts.StatsListenAddr,
153+
ServiceName: h.currentConsulConfig.ServiceName,
154+
ServiceID: h.currentConsulConfig.ServiceID,
155+
})
179156

180-
for range time.Tick(time.Minute) {
181-
reg()
182-
}
183-
}()
184-
go (&Stats{
185-
dpapi: h.dataplaneClient,
186-
service: h.currentConsulConfig.ServiceName,
187-
}).Run()
188157
go func() {
189-
http.Handle("/metrics", promhttp.Handler())
190-
191-
log.Infof("Starting stats server at %s", h.opts.StatsListenAddr)
192-
err := http.ListenAndServe(h.opts.StatsListenAddr, nil)
158+
err := s.Run()
193159
if err != nil {
194-
log.Errorf("error starting stats server: %s", err)
160+
log.Error(err)
195161
}
196162
}()
197163

haproxy/state.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func (h *HAProxy) watch(sd *lib.Shutdown) error {
2525
var currentConfig consul.Config
2626
dirty := false
2727
started := false
28+
ready := false
2829

2930
waitAndRetry := func() {
3031
time.Sleep(retryBackoff)
@@ -69,7 +70,6 @@ func (h *HAProxy) watch(sd *lib.Shutdown) error {
6970
return err
7071
}
7172
started = true
72-
close(h.Ready)
7373
}
7474

7575
if dirty {
@@ -122,6 +122,11 @@ func (h *HAProxy) watch(sd *lib.Shutdown) error {
122122
continue
123123
}
124124

125+
if !ready {
126+
close(h.Ready)
127+
ready = true
128+
}
129+
125130
currentState = newState
126131
log.Info("state applied")
127132
}

haproxy/stats.go renamed to haproxy/stats/metrics.go

+28-34
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
package haproxy
1+
package stats
22

33
import (
44
"strings"
55
"time"
66

7-
"github.com/haproxytech/haproxy-consul-connect/haproxy/dataplane"
87
"github.com/haproxytech/models"
98
"github.com/prometheus/client_golang/prometheus"
109
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -70,13 +69,8 @@ var (
7069
}, []string{"service"})
7170
)
7271

73-
type Stats struct {
74-
service string
75-
dpapi *dataplane.Dataplane
76-
}
77-
78-
func (s *Stats) Run() {
79-
upMetric.WithLabelValues(s.service).Set(1)
72+
func (s *Stats) runMetrics() {
73+
upMetric.WithLabelValues(s.cfg.ServiceName).Set(1)
8074
for {
8175
time.Sleep(time.Second)
8276
stats, err := s.dpapi.Stats()
@@ -114,42 +108,42 @@ func (s *Stats) handleFrontend(stats *models.NativeStat) {
114108
targetService := strings.TrimPrefix(stats.Name, "front_")
115109

116110
if targetService == "downstream" {
117-
reqInRate.WithLabelValues(s.service).Set(statVal(stats.Stats.ReqRate))
118-
connInCount.WithLabelValues(s.service).Set(statVal(stats.Stats.Scur))
119-
bytesInIn.WithLabelValues(s.service).Set(statVal(stats.Stats.Bin))
120-
bytesOutIn.WithLabelValues(s.service).Set(statVal(stats.Stats.Bout))
121-
122-
resInTotal.WithLabelValues(s.service, "1xx").Set(statVal(stats.Stats.Hrsp1xx))
123-
resInTotal.WithLabelValues(s.service, "2xx").Set(statVal(stats.Stats.Hrsp2xx))
124-
resInTotal.WithLabelValues(s.service, "3xx").Set(statVal(stats.Stats.Hrsp3xx))
125-
resInTotal.WithLabelValues(s.service, "4xx").Set(statVal(stats.Stats.Hrsp4xx))
126-
resInTotal.WithLabelValues(s.service, "5xx").Set(statVal(stats.Stats.Hrsp5xx))
127-
resInTotal.WithLabelValues(s.service, "other").Set(statVal(stats.Stats.HrspOther))
111+
reqInRate.WithLabelValues(s.cfg.ServiceName).Set(statVal(stats.Stats.ReqRate))
112+
connInCount.WithLabelValues(s.cfg.ServiceName).Set(statVal(stats.Stats.Scur))
113+
bytesInIn.WithLabelValues(s.cfg.ServiceName).Set(statVal(stats.Stats.Bin))
114+
bytesOutIn.WithLabelValues(s.cfg.ServiceName).Set(statVal(stats.Stats.Bout))
115+
116+
resInTotal.WithLabelValues(s.cfg.ServiceName, "1xx").Set(statVal(stats.Stats.Hrsp1xx))
117+
resInTotal.WithLabelValues(s.cfg.ServiceName, "2xx").Set(statVal(stats.Stats.Hrsp2xx))
118+
resInTotal.WithLabelValues(s.cfg.ServiceName, "3xx").Set(statVal(stats.Stats.Hrsp3xx))
119+
resInTotal.WithLabelValues(s.cfg.ServiceName, "4xx").Set(statVal(stats.Stats.Hrsp4xx))
120+
resInTotal.WithLabelValues(s.cfg.ServiceName, "5xx").Set(statVal(stats.Stats.Hrsp5xx))
121+
resInTotal.WithLabelValues(s.cfg.ServiceName, "other").Set(statVal(stats.Stats.HrspOther))
128122
} else {
129-
reqOutRate.WithLabelValues(s.service, targetService).Set(statVal(stats.Stats.ReqRate))
130-
connOutCount.WithLabelValues(s.service, targetService).Set(statVal(stats.Stats.Scur))
131-
bytesInOut.WithLabelValues(s.service, targetService).Set(statVal(stats.Stats.Bin))
132-
bytesOutOut.WithLabelValues(s.service, targetService).Set(statVal(stats.Stats.Bout))
133-
134-
resOutTotal.WithLabelValues(s.service, targetService, "1xx").Set(statVal(stats.Stats.Hrsp1xx))
135-
resOutTotal.WithLabelValues(s.service, targetService, "2xx").Set(statVal(stats.Stats.Hrsp2xx))
136-
resOutTotal.WithLabelValues(s.service, targetService, "3xx").Set(statVal(stats.Stats.Hrsp3xx))
137-
resOutTotal.WithLabelValues(s.service, targetService, "4xx").Set(statVal(stats.Stats.Hrsp4xx))
138-
resOutTotal.WithLabelValues(s.service, targetService, "5xx").Set(statVal(stats.Stats.Hrsp5xx))
139-
resOutTotal.WithLabelValues(s.service, targetService, "other").Set(statVal(stats.Stats.HrspOther))
123+
reqOutRate.WithLabelValues(s.cfg.ServiceName, targetService).Set(statVal(stats.Stats.ReqRate))
124+
connOutCount.WithLabelValues(s.cfg.ServiceName, targetService).Set(statVal(stats.Stats.Scur))
125+
bytesInOut.WithLabelValues(s.cfg.ServiceName, targetService).Set(statVal(stats.Stats.Bin))
126+
bytesOutOut.WithLabelValues(s.cfg.ServiceName, targetService).Set(statVal(stats.Stats.Bout))
127+
128+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "1xx").Set(statVal(stats.Stats.Hrsp1xx))
129+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "2xx").Set(statVal(stats.Stats.Hrsp2xx))
130+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "3xx").Set(statVal(stats.Stats.Hrsp3xx))
131+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "4xx").Set(statVal(stats.Stats.Hrsp4xx))
132+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "5xx").Set(statVal(stats.Stats.Hrsp5xx))
133+
resOutTotal.WithLabelValues(s.cfg.ServiceName, targetService, "other").Set(statVal(stats.Stats.HrspOther))
140134
}
141135
}
142136

143137
func (s *Stats) handlebackend(stats *models.NativeStat) {
144138
targetService := strings.TrimPrefix(stats.Name, "back_")
145139

146140
if targetService == "downstream" {
147-
resTimeIn.WithLabelValues(s.service).Set(statVal(stats.Stats.Ttime) / 1000)
141+
resTimeIn.WithLabelValues(s.cfg.ServiceName).Set(statVal(stats.Stats.Ttime) / 1000)
148142
} else {
149-
resTimeOut.WithLabelValues(s.service, targetService).Set(statVal(stats.Stats.Ttime) / 1000)
143+
resTimeOut.WithLabelValues(s.cfg.ServiceName, targetService).Set(statVal(stats.Stats.Ttime) / 1000)
150144
}
151145
}
152146

153147
func (s *Stats) handleServer(stats *models.NativeStat) {
154-
resTimeOut.WithLabelValues(s.service, stats.Name).Set(statVal(stats.Stats.Ttime) / 1000)
148+
resTimeOut.WithLabelValues(s.cfg.ServiceName, stats.Name).Set(statVal(stats.Stats.Ttime) / 1000)
155149
}

0 commit comments

Comments
 (0)