Skip to content
This repository was archived by the owner on May 17, 2024. It is now read-only.

Commit d91c8c1

Browse files
committed
Count failed API calls.
This adds an additional metric for counting failed API calls to the Git hosting service. A failed call is one that causes either an error, or with the response status >= 400.
1 parent f271e71 commit d91c8c1

File tree

6 files changed

+57
-11
lines changed

6 files changed

+57
-11
lines changed

pkg/git/client.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ func (c *SCMClient) FileContents(ctx context.Context, repo, path, ref string) ([
5555
func (c *SCMClient) CreateStatus(ctx context.Context, repo, commit string, s *scm.StatusInput) error {
5656
c.m.CountAPICall("create_status")
5757
_, r, err := c.client.Repositories.CreateStatus(ctx, repo, commit, s)
58-
if isErrorStatus(r.Status) {
58+
errResponse := isErrorStatus(r.Status)
59+
if errResponse || err != nil {
60+
c.m.CountFailedAPICall("file_contents")
61+
}
62+
if errResponse {
5963
return scmError{msg: fmt.Sprintf("failed to create commitstatus in repo %s commit %s", repo, commit), Status: r.Status}
6064
}
6165
return err
6266
}
6367

6468
func isErrorStatus(i int) bool {
65-
return i > 400
69+
return i >= 400
6670
}

pkg/git/client_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,14 @@ func TestCreateStatus(t *testing.T) {
117117
}
118118

119119
func TestCreateStatusWithNotFoundResponse(t *testing.T) {
120+
m := metrics.NewMock()
120121
as := makeAPIServer(t, "/api/v3/repos/Codertocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e", "", "")
121122
defer as.Close()
122123
scmClient, err := factory.NewClient("github", as.URL, "", factory.Client(as.Client()))
123124
if err != nil {
124125
t.Fatal(err)
125126
}
126-
client := New(scmClient, nil, metrics.NewMock())
127+
client := New(scmClient, nil, m)
127128

128129
status := &scm.StatusInput{
129130
State: scm.StatePending,
@@ -135,6 +136,9 @@ func TestCreateStatusWithNotFoundResponse(t *testing.T) {
135136
if !IsNotFound(err) {
136137
t.Fatal(err)
137138
}
139+
if m.FailedAPICalls != 1 {
140+
t.Fatalf("metrics count of failed API calls, got %d, want 1", m.FailedAPICalls)
141+
}
138142
}
139143

140144
func makeAPIServer(t *testing.T, urlPath, ref, fixture string) *httptest.Server {

pkg/metrics/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ type Interface interface {
1212

1313
// CountAPICall records API calls to the upstream hosting service.
1414
CountAPICall(name string)
15+
16+
// CountFailedAPICall records failed API calls to the upstream hosting service.
17+
CountFailedAPICall(name string)
1518
}

pkg/metrics/metrics.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import (
88
// PrometheusMetrics is a wrapper around Prometheus metrics for counting
99
// events in the system.
1010
type PrometheusMetrics struct {
11-
hooks *prometheus.CounterVec
12-
invalidHooks prometheus.Counter
13-
apiCalls *prometheus.CounterVec
11+
hooks *prometheus.CounterVec
12+
invalidHooks prometheus.Counter
13+
apiCalls *prometheus.CounterVec
14+
failedAPICalls *prometheus.CounterVec
1415
}
1516

1617
// New creates and returns a PrometheusMetrics initialised with prometheus
@@ -39,9 +40,16 @@ func New(ns string, reg prometheus.Registerer) *PrometheusMetrics {
3940
Help: "Count of API Calls made",
4041
}, []string{"kind"})
4142

43+
pm.failedAPICalls = prometheus.NewCounterVec(prometheus.CounterOpts{
44+
Namespace: ns,
45+
Name: "failed_api_calls_total",
46+
Help: "Count of failed API Calls made",
47+
}, []string{"kind"})
48+
4249
reg.MustRegister(pm.hooks)
4350
reg.MustRegister(pm.invalidHooks)
4451
reg.MustRegister(pm.apiCalls)
52+
reg.MustRegister(pm.failedAPICalls)
4553
return pm
4654
}
4755

@@ -59,3 +67,8 @@ func (m *PrometheusMetrics) CountInvalidHook() {
5967
func (m *PrometheusMetrics) CountAPICall(name string) {
6068
m.apiCalls.With(prometheus.Labels{"kind": name}).Inc()
6169
}
70+
71+
// CountFailedAPICall records failled outgoing API calls to upstream services.
72+
func (m *PrometheusMetrics) CountFailedAPICall(name string) {
73+
m.failedAPICalls.With(prometheus.Labels{"kind": name}).Inc()
74+
}

pkg/metrics/metrics_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,17 @@ dsl_api_calls_total{kind="file_contents"} 1
5555
t.Fatal(err)
5656
}
5757
}
58+
59+
func TestCountFailedAPICall(t *testing.T) {
60+
m := New("dsl", prometheus.NewRegistry())
61+
m.CountFailedAPICall("commit_status")
62+
63+
err := testutil.CollectAndCompare(m.failedAPICalls, strings.NewReader(`
64+
# HELP dsl_failed_api_calls_total Count of failed API Calls made
65+
# TYPE dsl_failed_api_calls_total counter
66+
dsl_failed_api_calls_total{kind="commit_status"} 1
67+
`))
68+
if err != nil {
69+
t.Fatal(err)
70+
}
71+
}

pkg/metrics/mock.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import (
44
"github.com/jenkins-x/go-scm/scm"
55
)
66

7-
// MockMetrics is a value that provides a wrapper around Mock
8-
// metrics for counting events in the system.
7+
var _ Interface = (*MockMetrics)(nil)
8+
9+
// MockMetrics is a type that provides a simple counter for metrics for test
10+
// purposes.
911
type MockMetrics struct {
10-
Hooks int
11-
InvalidHooks int
12-
APICalls int
12+
Hooks int
13+
InvalidHooks int
14+
APICalls int
15+
FailedAPICalls int
1316
}
1417

1518
// NewMock creates and returns a MockMetrics.
@@ -31,3 +34,8 @@ func (m *MockMetrics) CountInvalidHook() {
3134
func (m *MockMetrics) CountAPICall(name string) {
3235
m.APICalls++
3336
}
37+
38+
// CountFailedAPICall records failed outgoing API calls to upstream services.
39+
func (m *MockMetrics) CountFailedAPICall(name string) {
40+
m.FailedAPICalls++
41+
}

0 commit comments

Comments
 (0)