Skip to content

Commit

Permalink
feat: metrics endpoint basic auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsuribori committed Nov 14, 2023
1 parent c5f3792 commit c029932
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 47 deletions.
20 changes: 18 additions & 2 deletions docs/advanced/metrics.md
Expand Up @@ -9,10 +9,26 @@ How to configure metrics is explained in the [Observability configuration refere

```yaml
metrics-enabled: true
metrics-exporter: "prometheus"
metrics-auth-enabled: true
metrics-auth-username: some_username
metrics-auth-password: some_password
```

This will expose the metrics under the **public** endpoint `/metrics`.
This will expose the metrics under the endpoint `/metrics`, protected with HTTP Basic Authentication.

A following is an example how to configure a job in Prometheus `scrape_configs`:

```yaml
- job_name: gotosocial
metrics_path: /metrics
scheme: https
basic_auth:
username: some_username
password: some_password
static_configs:
- targets:
- example.org
```

[otel]: https://opentelemetry.io/
[obs]: ../configuration/observability.md
16 changes: 11 additions & 5 deletions docs/configuration/observability.md
Expand Up @@ -39,9 +39,15 @@ tracing-insecure-transport: false
# Default: false
metrics-enabled: false

# String. The OpenTelemetry metrics exporter to use. Only "prometheus" for Prometheus pull is currently supported.
# Exposes a public /metrics endpoint.
# Examples: ["prometheus"]
# Default: "prometheus"
metrics-exporter: "prometheus"
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
# Default: false
metrics-auth-enabled: false

# String. Username for Prometheus metrics endpoint.
# Default: ""
metrics-auth-username: ""

# String. Password for Prometheus metrics endpoint.
# Default: ""
metrics-auth-password: ""
```
16 changes: 11 additions & 5 deletions example/config.yaml
Expand Up @@ -782,11 +782,17 @@ tracing-insecure-transport: false
# Default: false
metrics-enabled: false

# String. The OpenTelemetry metrics exporter to use. Only "prometheus" for Prometheus pull is currently supported.
# Exposes a public /metrics endpoint.
# Examples: ["prometheus"]
# Default: "prometheus"
metrics-exporter: "prometheus"
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
# Default: false
metrics-auth-enabled: false

# String. Username for Prometheus metrics endpoint.
# Default: ""
metrics-auth-username: ""

# String. Password for Prometheus metrics endpoint.
# Default: ""
metrics-auth-password: ""

################################
##### HTTP CLIENT SETTINGS #####
Expand Down
6 changes: 4 additions & 2 deletions internal/config/config.go
Expand Up @@ -138,8 +138,10 @@ type Configuration struct {
TracingEndpoint string `name:"tracing-endpoint" usage:"Endpoint of your trace collector. Eg., 'localhost:4317' for gRPC, 'localhost:4318' for http"`
TracingInsecureTransport bool `name:"tracing-insecure-transport" usage:"Disable TLS for the gRPC or HTTP transport protocol"`

MetricsEnabled bool `name:"metrics-enabled" usage:"Enable OTLP metrics"`
MetricsExporter string `name:"metrics-exporter" usage:"prometheus"`
MetricsEnabled bool `name:"metrics-enabled" usage:"Enable OpenTelemetry based metrics support."`
MetricsAuthEnabled bool `name:"metrics-auth-enabled" usage:"Enable HTTP Basic Authentication for Prometheus metrics endpoint"`
MetricsAuthUsername string `name:"metrics-auth-username" usage:"Username for Prometheus metrics endpoint"`
MetricsAuthPassword string `name:"metrics-auth-password" usage:"Password for Prometheus metrics endpoint"`

SMTPHost string `name:"smtp-host" usage:"Host of the smtp server. Eg., 'smtp.eu.mailgun.org'"`
SMTPPort int `name:"smtp-port" usage:"Port of the smtp server. Eg., 587"`
Expand Down
4 changes: 2 additions & 2 deletions internal/config/defaults.go
Expand Up @@ -119,8 +119,8 @@ var Defaults = Configuration{
TracingEndpoint: "",
TracingInsecureTransport: false,

MetricsEnabled: false,
MetricsExporter: "prometheus",
MetricsEnabled: false,
MetricsAuthEnabled: true,

SyslogEnabled: false,
SyslogProtocol: "udp",
Expand Down
77 changes: 64 additions & 13 deletions internal/config/helpers.gen.go
Expand Up @@ -2,7 +2,7 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
Expand Down Expand Up @@ -2099,30 +2099,80 @@ func GetMetricsEnabled() bool { return global.GetMetricsEnabled() }
// SetMetricsEnabled safely sets the value for global configuration 'MetricsEnabled' field
func SetMetricsEnabled(v bool) { global.SetMetricsEnabled(v) }

// GetMetricsExporter safely fetches the Configuration value for state's 'MetricsExporter' field
func (st *ConfigState) GetMetricsExporter() (v string) {
// GetMetricsAuthEnabled safely fetches the Configuration value for state's 'MetricsAuthEnabled' field
func (st *ConfigState) GetMetricsAuthEnabled() (v bool) {
st.mutex.RLock()
v = st.config.MetricsAuthEnabled
st.mutex.RUnlock()
return
}

// SetMetricsAuthEnabled safely sets the Configuration value for state's 'MetricsAuthEnabled' field
func (st *ConfigState) SetMetricsAuthEnabled(v bool) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.MetricsAuthEnabled = v
st.reloadToViper()
}

// MetricsAuthEnabledFlag returns the flag name for the 'MetricsAuthEnabled' field
func MetricsAuthEnabledFlag() string { return "metrics-auth-enabled" }

// GetMetricsAuthEnabled safely fetches the value for global configuration 'MetricsAuthEnabled' field
func GetMetricsAuthEnabled() bool { return global.GetMetricsAuthEnabled() }

// SetMetricsAuthEnabled safely sets the value for global configuration 'MetricsAuthEnabled' field
func SetMetricsAuthEnabled(v bool) { global.SetMetricsAuthEnabled(v) }

// GetMetricsAuthUsername safely fetches the Configuration value for state's 'MetricsAuthUsername' field
func (st *ConfigState) GetMetricsAuthUsername() (v string) {
st.mutex.RLock()
v = st.config.MetricsExporter
v = st.config.MetricsAuthUsername
st.mutex.RUnlock()
return
}

// SetMetricsExporter safely sets the Configuration value for state's 'MetricsExporter' field
func (st *ConfigState) SetMetricsExporter(v string) {
// SetMetricsAuthUsername safely sets the Configuration value for state's 'MetricsAuthUsername' field
func (st *ConfigState) SetMetricsAuthUsername(v string) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.MetricsExporter = v
st.config.MetricsAuthUsername = v
st.reloadToViper()
}

// MetricsExporterFlag returns the flag name for the 'MetricsExporter' field
func MetricsExporterFlag() string { return "metrics-exporter" }
// MetricsAuthUsernameFlag returns the flag name for the 'MetricsAuthUsername' field
func MetricsAuthUsernameFlag() string { return "metrics-auth-username" }

// GetMetricsExporter safely fetches the value for global configuration 'MetricsExporter' field
func GetMetricsExporter() string { return global.GetMetricsExporter() }
// GetMetricsAuthUsername safely fetches the value for global configuration 'MetricsAuthUsername' field
func GetMetricsAuthUsername() string { return global.GetMetricsAuthUsername() }

// SetMetricsExporter safely sets the value for global configuration 'MetricsExporter' field
func SetMetricsExporter(v string) { global.SetMetricsExporter(v) }
// SetMetricsAuthUsername safely sets the value for global configuration 'MetricsAuthUsername' field
func SetMetricsAuthUsername(v string) { global.SetMetricsAuthUsername(v) }

// GetMetricsAuthPassword safely fetches the Configuration value for state's 'MetricsAuthPassword' field
func (st *ConfigState) GetMetricsAuthPassword() (v string) {
st.mutex.RLock()
v = st.config.MetricsAuthPassword
st.mutex.RUnlock()
return
}

// SetMetricsAuthPassword safely sets the Configuration value for state's 'MetricsAuthPassword' field
func (st *ConfigState) SetMetricsAuthPassword(v string) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.MetricsAuthPassword = v
st.reloadToViper()
}

// MetricsAuthPasswordFlag returns the flag name for the 'MetricsAuthPassword' field
func MetricsAuthPasswordFlag() string { return "metrics-auth-password" }

// GetMetricsAuthPassword safely fetches the value for global configuration 'MetricsAuthPassword' field
func GetMetricsAuthPassword() string { return global.GetMetricsAuthPassword() }

// SetMetricsAuthPassword safely sets the value for global configuration 'MetricsAuthPassword' field
func SetMetricsAuthPassword(v string) { global.SetMetricsAuthPassword(v) }

// GetSMTPHost safely fetches the Configuration value for state's 'SMTPHost' field
func (st *ConfigState) GetSMTPHost() (v string) {
Expand Down Expand Up @@ -3598,3 +3648,4 @@ func GetRequestIDHeader() string { return global.GetRequestIDHeader() }

// SetRequestIDHeader safely sets the value for global configuration 'RequestIDHeader' field
func SetRequestIDHeader(v string) { global.SetRequestIDHeader(v) }

29 changes: 16 additions & 13 deletions internal/metrics/metrics.go
Expand Up @@ -20,7 +20,7 @@
package metrics

import (
"fmt"
"errors"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/config"
Expand All @@ -43,7 +43,12 @@ func Initialize() error {
return nil
}

fmt.Println("Initializing metrics")
if config.GetMetricsAuthEnabled() {
if config.GetMetricsAuthPassword() == "" || config.GetMetricsAuthUsername() == "" {
return errors.New("metrics-auth-username and metrics-auth-password must be set when metrics-auth-enabled is true")
}
}

r, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
Expand All @@ -52,19 +57,17 @@ func Initialize() error {
),
)

if config.GetMetricsExporter() == "prometheus" {
prometheusExporter, err := prometheus.New()
if err != nil {
return err
}

meterProvider := sdk.NewMeterProvider(
sdk.WithResource(r),
sdk.WithReader(prometheusExporter),
)
otel.SetMeterProvider(meterProvider)
prometheusExporter, err := prometheus.New()
if err != nil {
return err
}

meterProvider := sdk.NewMeterProvider(
sdk.WithResource(r),
sdk.WithReader(prometheusExporter),
)
otel.SetMeterProvider(meterProvider)

return nil
}

Expand Down
1 change: 1 addition & 0 deletions internal/web/metrics.go
Expand Up @@ -24,6 +24,7 @@ import (

const (
metricsPath = "/metrics"
metricsUser = "metrics"
)

func (m *Module) metricsGETHandler(c *gin.Context) {
Expand Down
10 changes: 8 additions & 2 deletions internal/web/web.go
Expand Up @@ -112,9 +112,15 @@ func (m *Module) Route(r router.Router, mi ...gin.HandlerFunc) {

// Prometheus metrics export endpoint
if config.GetMetricsEnabled() {
if config.GetMetricsExporter() == "prometheus" {
r.AttachHandler(http.MethodGet, metricsPath, m.metricsGETHandler)
metricsGroup := r.AttachGroup(metricsPath)
metricsGroup.Use(mi...)
// Attach basic auth if enabled
if config.GetMetricsAuthEnabled() {
metricsGroup.Use(gin.BasicAuth(gin.Accounts{
config.GetMetricsAuthUsername(): config.GetMetricsAuthPassword(),
}))
}
metricsGroup.Handle(http.MethodGet, "", m.metricsGETHandler)
}

// Attach redirects from old endpoints to current ones for backwards compatibility
Expand Down
4 changes: 3 additions & 1 deletion test/envparsing.sh
Expand Up @@ -104,7 +104,9 @@ EXPECT=$(cat << "EOF"
"media-remote-cache-days": 30,
"media-video-max-size": 420,
"metrics-enabled": false,
"metrics-exporter": "prometheus",
"metrics-auth-enabled": false,
"metrics-auth-username": "",
"metrics-auth-password": "",
"oidc-admin-groups": [
"steamy"
],
Expand Down
4 changes: 2 additions & 2 deletions testrig/config.go
Expand Up @@ -123,8 +123,8 @@ var testDefaults = config.Configuration{
TracingTransport: "grpc",
TracingInsecureTransport: true,

MetricsEnabled: false,
MetricsExporter: "prometheus",
MetricsEnabled: false,
MetricsAuthEnabled: false,

SyslogEnabled: false,
SyslogProtocol: "udp",
Expand Down

0 comments on commit c029932

Please sign in to comment.