-
Notifications
You must be signed in to change notification settings - Fork 366
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Julio Chana
committed
Feb 19, 2018
1 parent
6b4f8f4
commit fd06bce
Showing
6 changed files
with
206 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package metrics | ||
|
||
// Dummy is a handy instnce of a dummy instrumenter, most of the times it will be used on tests. | ||
var Dummy = &dummy{} | ||
|
||
// dummy is a dummy implementation of Instrumenter. | ||
type dummy struct{} | ||
|
||
func (d *dummy) SetClusterOK(namespace string, name string) {} | ||
func (d *dummy) SetClusterError(namespace string, name string) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package metrics | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/prometheus/client_golang/prometheus/promhttp" | ||
) | ||
|
||
const ( | ||
promNamespace = "redis_operator" | ||
promControllerSubsystem = "controller" | ||
) | ||
|
||
// Instrumenter is the interface that will collect the metrics and has ability to send/expose those metrics. | ||
type Instrumenter interface { | ||
SetClusterOK(namespace string, name string) | ||
SetClusterError(namespace string, name string) | ||
} | ||
|
||
// PromMetrics implements the instrumenter so the metrics can be managed by Prometheus. | ||
type PromMetrics struct { | ||
// Metrics fields. | ||
clusterOK *prometheus.GaugeVec // clusterOk is the status of a cluster | ||
|
||
// Instrumentation fields. | ||
registry prometheus.Registerer | ||
path string | ||
mux *http.ServeMux | ||
} | ||
|
||
// NewPrometheusMetrics returns a new PromMetrics object. | ||
func NewPrometheusMetrics(path string, mux *http.ServeMux) *PromMetrics { | ||
// Create metrics. | ||
clusterOK := prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
Namespace: promNamespace, | ||
Subsystem: promControllerSubsystem, | ||
Name: "cluster_ok", | ||
Help: "Number of failover clusters managed by the operator.", | ||
}, []string{"namespace", "name"}) | ||
|
||
// Create Prometheus registry, use this instead of the prometheus default registry. | ||
// TODO: Do we need go default instrumentation bout the go process metrics? | ||
promReg := prometheus.NewRegistry() | ||
|
||
// Create the instance. | ||
p := &PromMetrics{ | ||
clusterOK: clusterOK, | ||
|
||
registry: promReg, | ||
path: path, | ||
mux: mux, | ||
} | ||
|
||
// Register metrics on prometheus. | ||
p.register() | ||
|
||
// Register prometheus handler so we can serve the metrics. | ||
handler := promhttp.HandlerFor(promReg, promhttp.HandlerOpts{}) | ||
mux.Handle(path, handler) | ||
|
||
return p | ||
} | ||
|
||
// register will register all the required prometheus metrics on the Prometheus collector. | ||
func (p *PromMetrics) register() { | ||
p.registry.MustRegister(p.clusterOK) | ||
} | ||
|
||
// SetClusterOK set the cluster status to OK | ||
func (p *PromMetrics) SetClusterOK(namespace string, name string) { | ||
p.clusterOK.WithLabelValues(namespace, name).Set(1) | ||
} | ||
|
||
// SetClusterError set the cluster status to Error | ||
func (p *PromMetrics) SetClusterError(namespace string, name string) { | ||
p.clusterOK.WithLabelValues(namespace, name).Set(0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package metrics_test | ||
|
||
import ( | ||
"io/ioutil" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/spotahome/redis-operator/metrics" | ||
) | ||
|
||
func TestPrometheusMetrics(t *testing.T) { | ||
|
||
tests := []struct { | ||
name string | ||
addMetrics func(pm *metrics.PromMetrics) | ||
expMetrics []string | ||
expCode int | ||
}{ | ||
{ | ||
name: "Setting OK should give an OK", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterOK("testns", "test") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns"} 1`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
{ | ||
name: "Setting Error should give an Error", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterError("testns", "test") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns"} 0`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
{ | ||
name: "Setting Error after ok should give an Error", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterOK("testns", "test") | ||
pm.SetClusterError("testns", "test") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns"} 0`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
{ | ||
name: "Setting OK after Error should give an OK", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterError("testns", "test") | ||
pm.SetClusterOK("testns", "test") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns"} 1`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
{ | ||
name: "Multiple clusters should appear", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterOK("testns", "test") | ||
pm.SetClusterOK("testns", "test2") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns"} 1`, | ||
`redis_operator_controller_cluster_ok{name="test2",namespace="testns"} 1`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
{ | ||
name: "Same name on different namespaces should appear", | ||
addMetrics: func(pm *metrics.PromMetrics) { | ||
pm.SetClusterOK("testns1", "test") | ||
pm.SetClusterOK("testns2", "test") | ||
}, | ||
expMetrics: []string{ | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns1"} 1`, | ||
`redis_operator_controller_cluster_ok{name="test",namespace="testns2"} 1`, | ||
}, | ||
expCode: http.StatusOK, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
path := "/awesome-metrics" | ||
|
||
// Create the muxer for testing. | ||
mx := http.NewServeMux() | ||
pm := metrics.NewPrometheusMetrics(path, mx) | ||
|
||
// Add metrics to prometheus. | ||
test.addMetrics(pm) | ||
|
||
// Make the request to the metrics. | ||
req := httptest.NewRequest("GET", path, nil) | ||
w := httptest.NewRecorder() | ||
mx.ServeHTTP(w, req) | ||
|
||
resp := w.Result() | ||
if assert.Equal(test.expCode, resp.StatusCode) { | ||
body, _ := ioutil.ReadAll(resp.Body) | ||
// Check all the metrics are present. | ||
for _, expMetric := range test.expMetrics { | ||
assert.Contains(string(body), expMetric) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.