Skip to content

Commit a0d3847

Browse files
Abdul Qadeerzorro786
authored andcommitted
Add health check API (#41)
* Uptick go version * Add test stage * Add health check for metric provider servers * Address review comments Signed-off-by: Abdul Qadeer <aqadeer@paypal.com> (cherry picked from commit 32392ed)
1 parent 26767fe commit a0d3847

File tree

9 files changed

+84
-2
lines changed

9 files changed

+84
-2
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ jobs:
2020

2121
- name: Build
2222
run: go build -o load-watcher main.go
23+
24+
- name: Test
25+
run: go test ./...

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/paypal/load-watcher
22

3-
go 1.15
3+
go 1.16
44

55
require (
66
github.com/francoispqt/gojay v1.2.13

pkg/watcher/internal/metricsprovider/k8s.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package metricsprovider
1818

1919
import (
2020
"context"
21+
"fmt"
22+
"net/http"
2123
"os"
2224

2325
"github.com/paypal/load-watcher/pkg/watcher"
@@ -153,3 +155,12 @@ func (m metricsServerClient) FetchAllHostsMetrics(window *watcher.Window) (map[s
153155

154156
return metrics, nil
155157
}
158+
159+
func (m metricsServerClient) Health() (int, error) {
160+
var status int
161+
m.metricsClientSet.RESTClient().Verb("HEAD").Do(context.Background()).StatusCode(&status)
162+
if status != http.StatusOK {
163+
return -1, fmt.Errorf("received response status code: %v", status)
164+
}
165+
return 0, nil
166+
}

pkg/watcher/internal/metricsprovider/prometheus.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package metricsprovider
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
2223
"time"
2324

2425
"github.com/paypal/load-watcher/pkg/watcher"
@@ -133,6 +134,21 @@ func (s promClient) FetchAllHostsMetrics(window *watcher.Window) (map[string][]w
133134
return hostMetrics, anyerr
134135
}
135136

137+
func (s promClient) Health() (int, error) {
138+
req, err := http.NewRequest("HEAD", DefaultPromAddress, nil)
139+
if err != nil {
140+
return -1, err
141+
}
142+
resp, _, err := s.client.Do(context.Background(), req)
143+
if err != nil {
144+
return -1, err
145+
}
146+
if resp.StatusCode != http.StatusOK {
147+
return -1, fmt.Errorf("received response status code: %v", resp.StatusCode)
148+
}
149+
return 0, nil
150+
}
151+
136152
func (s promClient) buildPromQuery(host string, metric string, method string, rollup string) string {
137153
var promQuery string
138154

pkg/watcher/internal/metricsprovider/signalfx.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,27 @@ func (s signalFxClient) FetchAllHostsMetrics(window *watcher.Window) (map[string
191191
return metrics, nil
192192
}
193193

194+
func (s signalFxClient) Health() (int, error) {
195+
return Ping(s.client, s.signalFxAddress)
196+
}
197+
198+
// Simple ping utility to a given URL
199+
// Returns -1 if unhealthy, 0 if healthy along with error if any
200+
func Ping(client http.Client, url string) (int, error) {
201+
req, err := http.NewRequest("HEAD", url, nil)
202+
if err != nil {
203+
return -1, err
204+
}
205+
resp, err := client.Do(req)
206+
if err != nil {
207+
return -1, err
208+
}
209+
if resp.StatusCode != http.StatusOK {
210+
return -1, fmt.Errorf("received response code: %v", resp.StatusCode)
211+
}
212+
return 0, nil
213+
}
214+
194215
func addMetadata(metric *watcher.Metric, metricType string) {
195216
metric.Operator = watcher.Average
196217
if metricType == cpuUtilizationMetric {

pkg/watcher/metricsprovider.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@ type MetricsProviderClient interface {
5050
FetchHostMetrics(host string, window *Window) ([]Metric, error)
5151
// Fetch metrics for all hosts
5252
FetchAllHostsMetrics(window *Window) (map[string][]Metric, error)
53+
// Get metric provider server health status
54+
// Returns 0 if healthy, -1 if unhealthy along with error if any
55+
Health() (int, error)
5356
}
5457

5558
// Generic metrics provider options
5659
type MetricsProviderOpts struct {
5760
Name string
5861
Address string
5962
AuthToken string
60-
}
63+
}

pkg/watcher/testserver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,7 @@ func (t testServerClient) FetchAllHostsMetrics(window *Window) (map[string][]Met
115115

116116
return FifteenMinutesMetricsMap, nil
117117
}
118+
119+
func (t testServerClient) Health() (int, error) {
120+
return 0, nil
121+
}

pkg/watcher/watcher.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737

3838
const (
3939
BaseUrl = "/watcher"
40+
HealthCheckUrl = "/watcher/health"
4041
FifteenMinutes = "15m"
4142
TenMinutes = "10m"
4243
FiveMinutes = "5m"
@@ -153,6 +154,7 @@ func (w *Watcher) StartWatching() {
153154
}
154155

155156
http.HandleFunc(BaseUrl, w.handler)
157+
http.HandleFunc(HealthCheckUrl, w.healthCheckHandler)
156158
server := &http.Server{
157159
Addr: ":2020",
158160
Handler: http.DefaultServeMux,
@@ -176,6 +178,7 @@ func (w *Watcher) StartWatching() {
176178
w.mutex.Lock()
177179
w.isStarted = true
178180
w.mutex.Unlock()
181+
log.Info("Started watching metrics")
179182
}
180183

181184
// StartWatching() should be called before calling this.
@@ -297,6 +300,16 @@ func (w *Watcher) handler(resp http.ResponseWriter, r *http.Request) {
297300
}
298301
}
299302

303+
// Simple server status handler
304+
func (w *Watcher) healthCheckHandler(resp http.ResponseWriter, r *http.Request) {
305+
if status, err := w.client.Health(); status != 0 {
306+
log.Warnf("health check failed with: %v", err)
307+
resp.WriteHeader(http.StatusServiceUnavailable)
308+
return
309+
}
310+
resp.WriteHeader(http.StatusOK)
311+
}
312+
300313
// Utility functions
301314

302315
func metricMapToWatcherMetrics(metricMap map[string][]Metric, clientName string, window Window) WatcherMetrics {

pkg/watcher/watcher_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ func TestWatcherInternalServerError(t *testing.T) {
120120
assert.Equal(t, http.StatusInternalServerError, rr.Code)
121121
}
122122

123+
func TestWatcherHealthCheck(t *testing.T) {
124+
req, err := http.NewRequest("GET", HealthCheckUrl, nil)
125+
require.Nil(t, err)
126+
127+
rr := httptest.NewRecorder()
128+
handler := http.HandlerFunc(w.handler)
129+
130+
handler.ServeHTTP(rr, req)
131+
require.Equal(t, http.StatusOK, rr.Code)
132+
}
133+
123134
func TestMain(m *testing.M) {
124135
client := NewTestMetricsServerClient()
125136
w = NewWatcher(client)

0 commit comments

Comments
 (0)