Skip to content

Commit

Permalink
Addresses review comments
Browse files Browse the repository at this point in the history
Creates a package for metrics where handlers are moved
  • Loading branch information
saltiyazan committed May 2, 2024
1 parent c83cf5b commit 8ae91a9
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 18 deletions.
25 changes: 11 additions & 14 deletions internal/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ import (
"net/http"
"strconv"
"strings"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// NewGoCertRouter takes in an environment struct, passes it along to any handlers that will need
// access to it, then builds and returns it for a server to consume
func NewGoCertRouter(env *Environment) http.Handler {
// access to it, and takes an http.Handler that will be used to handle metrics.
// then builds and returns it for a server to consume
func NewGoCertRouter(env *Environment, metricsHandler http.Handler) http.Handler {
router := http.NewServeMux()
router.HandleFunc("GET /certificate_requests", GetCertificateRequests(env))
router.HandleFunc("POST /certificate_requests", PostCertificateRequest(env))
Expand All @@ -26,15 +23,15 @@ func NewGoCertRouter(env *Environment) http.Handler {
router.HandleFunc("POST /certificate_requests/{id}/certificate/reject", RejectCertificate(env))
router.HandleFunc("DELETE /certificate_requests/{id}/certificate", DeleteCertificate(env))

v1 := http.NewServeMux()
v1.HandleFunc("GET /status", HealthCheck)
v1.Handle("/api/v1/", http.StripPrefix("/api/v1", router))
reg := prometheus.NewRegistry()
reg.MustRegister(collectors.NewGoCollector())
prometheusHandler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
v1.Handle("/api/v1/metrics", prometheusHandler)
monitoringMux := http.NewServeMux()
monitoringMux.HandleFunc("/status", HealthCheck)
monitoringMux.Handle("/metrics", metricsHandler)

api := http.NewServeMux()
api.Handle("/api/v1/", http.StripPrefix("/api/v1", router))
api.Handle("/", monitoringMux)

return logging(v1)
return logging(api)
}

// the health check endpoint simply returns a http.StatusOK
Expand Down
8 changes: 5 additions & 3 deletions internal/api/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

server "github.com/canonical/gocert/internal/api"
"github.com/canonical/gocert/internal/certdb"
metrics "github.com/canonical/gocert/internal/metrics"
)

const (
Expand Down Expand Up @@ -107,7 +108,8 @@ func TestGoCertRouter(t *testing.T) {
}
env := &server.Environment{}
env.DB = testdb
ts := httptest.NewTLSServer(server.NewGoCertRouter(env))
metricsHandler := metrics.NewPrometheusHandler()
ts := httptest.NewTLSServer(server.NewGoCertRouter(env, metricsHandler))
defer ts.Close()

client := ts.Client()
Expand Down Expand Up @@ -323,7 +325,7 @@ func TestGoCertRouter(t *testing.T) {
{
desc: "metrics endpoint success",
method: "GET",
path: "/api/v1/metrics",
path: "/metrics",
data: "",
response: "go_goroutines",
status: http.StatusOK,
Expand All @@ -345,7 +347,7 @@ func TestGoCertRouter(t *testing.T) {
t.Fatal(err)
}
switch path := tC.path; path {
case "/api/v1/metrics":
case "/metrics":
if res.StatusCode != tC.status || !strings.Contains(string(resBody), tC.response) {
t.Errorf("expected response did not match.\nExpected vs Received status code: %d vs %d\nExpected vs Received body: \n%s\nvs\n%s\n", tC.status, res.StatusCode, tC.response, string(resBody))
}
Expand Down
4 changes: 3 additions & 1 deletion internal/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

"github.com/canonical/gocert/internal/certdb"
metrics "github.com/canonical/gocert/internal/metrics"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -85,7 +86,8 @@ func NewServer(configFile string) (*http.Server, error) {

env := &Environment{}
env.DB = db
router := NewGoCertRouter(env)
metricsHandler := metrics.NewPrometheusHandler()
router := NewGoCertRouter(env, metricsHandler)

s := &http.Server{
Addr: fmt.Sprintf(":%d", config.Port),
Expand Down
32 changes: 32 additions & 0 deletions internal/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package metrics

import (
"net/http"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

type MetricsHandler interface {
http.Handler
}

// PrometheusHandler implements the MetricsHandler interface.
type PrometheusHandler struct {
registry *prometheus.Registry
}

// Returns a new PrometheusHandler.
func NewPrometheusHandler() MetricsHandler {
registry := prometheus.NewRegistry()
registry.MustRegister(collectors.NewGoCollector())
return &PrometheusHandler{
registry: registry,
}
}

// ServeHTTP implements the http.Handler interface, allowing the PrometheusHandler to handle HTTP requests.
func (p *PrometheusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
promhttp.HandlerFor(p.registry, promhttp.HandlerOpts{}).ServeHTTP(w, r)
}
33 changes: 33 additions & 0 deletions internal/metrics/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package metrics_test

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

metrics "github.com/canonical/gocert/internal/metrics"
)

// TestPrometheusHandler tests that the Prometheus metrics handler responds correctly to an HTTP request.
func TestPrometheusHandler(t *testing.T) {
handler := metrics.NewPrometheusHandler()

request, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatalf("could not create request: %v", err)
}

recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)

if status := recorder.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
if recorder.Body.String() == "" {
t.Errorf("handler returned an empty body")
}
if !strings.Contains(recorder.Body.String(), "go_goroutines") {
t.Errorf("handler returned an empty body")
}
}

0 comments on commit 8ae91a9

Please sign in to comment.