diff --git a/Dockerfile b/Dockerfile index a52b483..dcd0807 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN apk add --no-cache \ gnupg \ xz -EXPOSE 6157 2222 16157 +EXPOSE 6157 6158 2222 16157 RUN git config --global --add safe.directory /opengist RUN make install @@ -64,7 +64,7 @@ COPY --from=build --chown=opengist:opengist /opengist/config.yml /config.yml COPY --from=build --chown=opengist:opengist /opengist/opengist . COPY --from=build --chown=opengist:opengist /opengist/docker ./docker -EXPOSE 6157 2222 +EXPOSE 6157 6158 2222 VOLUME /opengist HEALTHCHECK --interval=60s --timeout=30s --start-period=15s --retries=3 CMD curl -f http://localhost:6157/healthcheck || exit 1 ENTRYPOINT ["./docker/entrypoint.sh"] diff --git a/config.yml b/config.yml index 48bf1a5..eaaa6ef 100644 --- a/config.yml +++ b/config.yml @@ -55,9 +55,15 @@ http.git-enabled: true # File permissions for Unix socket (octal format). Default: 0666 unix-socket-permissions: 0666 -# Enable or disable the metrics endpoint (either `true` or `false`). Default: false +# Enable or disable the Prometheus metrics server (either `true` or `false`). Default: false metrics.enabled: false +# The host on which the metrics server should bind. Default: 0.0.0.0 +metrics.host: 0.0.0.0 + +# The port on which the metrics server should listen. Default: 6158 +metrics.port: 6158 + # SSH built-in server configuration # Note: it is not using the SSH daemon from your machine (yet) diff --git a/docs/configuration/cheat-sheet.md b/docs/configuration/cheat-sheet.md index a012d67..544252a 100644 --- a/docs/configuration/cheat-sheet.md +++ b/docs/configuration/cheat-sheet.md @@ -21,7 +21,9 @@ aside: false | http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. | | http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) | | unix-socket-permissions | OG_UNIX_SOCKET_PERMISSIONS | `0666` | File permissions for Unix socket (octal format). | -| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics endpoint at `/metrics` (`true` or `false`) | +| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics server (`true` or `false`) | +| metrics.host | OG_METRICS_HOST | `0.0.0.0` | The host on which the metrics server should bind. | +| metrics.port | OG_METRICS_PORT | `6158` | The port on which the metrics server should listen. | | ssh.git-enabled | OG_SSH_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via SSH. (`true` or `false`) | | ssh.host | OG_SSH_HOST | `0.0.0.0` | The host on which the SSH server should bind. | | ssh.port | OG_SSH_PORT | `2222` | The port on which the SSH server should listen. | diff --git a/docs/configuration/metrics.md b/docs/configuration/metrics.md index 36eb22a..2f0451e 100644 --- a/docs/configuration/metrics.md +++ b/docs/configuration/metrics.md @@ -4,10 +4,10 @@ Opengist offers built-in support for Prometheus metrics to help you monitor the ## Enabling metrics -By default, the metrics endpoint is disabled for security and performance reasons. To enable it, update your configuration as stated in the [configuration cheat sheet](cheat-sheet.md): +By default, the metrics server is disabled for security and performance reasons. To enable it, update your configuration as stated in the [configuration cheat sheet](cheat-sheet.md): ```yaml -metrics.enabled = true +metrics.enabled: true ``` Alternatively, you can use the environment variable: @@ -16,7 +16,25 @@ Alternatively, you can use the environment variable: OG_METRICS_ENABLED=true ``` -Once enabled, metrics are available at the /metrics endpoint. +Once enabled, metrics are available on a separate server at `http://0.0.0.0:6158/metrics` by default. + +## Configuration + +The metrics server runs on a separate port from the main application. By default, it binds to `0.0.0.0` (all interfaces) on port `6158`. + +| Config Key | Environment Variable | Default | Description | +|----------------|---------------------|-------------|------------------------------------------------| +| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable the metrics server | +| metrics.host | OG_METRICS_HOST | `0.0.0.0` | The host on which the metrics server binds | +| metrics.port | OG_METRICS_PORT | `6158` | The port on which the metrics server listens | + +Example configuration: + +```yaml +metrics.enabled: true +metrics.host: 0.0.0.0 +metrics.port: 6158 +``` ## Available metrics @@ -36,14 +54,6 @@ These standard metrics follow the Prometheus naming convention and include label ## Security Considerations -The metrics endpoint exposes information about your Opengist instance that might be sensitive in some environments. Consider using a reverse proxy with authentication for the `/metrics` endpoint if your Opengist instance is publicly accessible. +The metrics server binds to `0.0.0.0` by default, making it accessible on all network interfaces. This default works well for containerized deployments (Docker, Kubernetes) where network isolation is handled at the infrastructure level. -Example with Nginx: - -```shell -location /metrics { - auth_basic "Metrics"; - auth_basic_user_file /etc/nginx/.htpasswd; - proxy_pass http://localhost:6157/metrics; -} -``` +For bare-metal or VM deployments where the metrics port may be exposed, consider restricting to localhost by setting `metrics.host: 127.0.0.1` to only allow local access. diff --git a/helm/opengist/README.md b/helm/opengist/README.md index 6d43360..cae74a6 100644 --- a/helm/opengist/README.md +++ b/helm/opengist/README.md @@ -6,6 +6,7 @@ Opengist Helm chart for Kubernetes. * [Install](#install) * [Configuration](#configuration) +* [Metrics & Monitoring](#metrics--monitoring) * [Dependencies](#dependencies) * [Meilisearch Indexer](#meilisearch-indexer) * [PostgreSQL Database](#postgresql-database) @@ -47,6 +48,76 @@ If defined, this existing secret will be used instead of creating a new one. configExistingSecret: ``` +## Metrics & Monitoring + +Opengist exposes Prometheus metrics on a separate port (default: `6158`). The metrics server runs independently from the main HTTP server for security. + +### Enabling Metrics + +To enable metrics, set `metrics.enabled: true` in your Opengist config: + +```yaml +config: + metrics.enabled: true +``` + +This will: +1. Start a metrics server on port 6158 inside the container +2. Create a Kubernetes Service exposing the metrics ports + +### Available Metrics + +| Metric Name | Type | Description | +|-------------|------|-------------| +| `opengist_users_total` | Gauge | Total number of registered users | +| `opengist_gists_total` | Gauge | Total number of gists | +| `opengist_ssh_keys_total` | Gauge | Total number of SSH keys | +| `opengist_request_duration_seconds_*` | Histogram | HTTP request duration metrics | + +### ServiceMonitor for Prometheus Operator + +If you're using [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator), you can enable automatic service discovery with a ServiceMonitor: + +```yaml +config: + metrics.enabled: true + +service: + metrics: + serviceMonitor: + enabled: true + labels: + release: prometheus # match your Prometheus serviceMonitorSelector +``` + +### Manual Prometheus Configuration + +If you're not using Prometheus Operator, you can configure Prometheus to scrape the metrics endpoint directly: + +```yaml +scrape_configs: + - job_name: 'opengist' + static_configs: + - targets: ['opengist-metrics:6158'] + metrics_path: /metrics +``` + +Or use Kubernetes service discovery: + +```yaml +scrape_configs: + - job_name: 'opengist' + kubernetes_sd_configs: + - role: service + relabel_configs: + - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_component] + regex: metrics + action: keep + - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name] + regex: opengist + action: keep +``` + ## Dependencies ### Meilisearch Indexer diff --git a/helm/opengist/templates/deployment.yaml b/helm/opengist/templates/deployment.yaml index 2983984..faf619f 100644 --- a/helm/opengist/templates/deployment.yaml +++ b/helm/opengist/templates/deployment.yaml @@ -67,6 +67,11 @@ spec: - name: http containerPort: {{ .Values.service.http.port }} protocol: TCP + {{- if index .Values.config "metrics.enabled" }} + - name: metrics + containerPort: {{ .Values.service.metrics.port }} + protocol: TCP + {{- end }} {{- if .Values.livenessProbe.enabled }} livenessProbe: {{- toYaml (omit .Values.livenessProbe "enabled") | nindent 12 }} diff --git a/helm/opengist/templates/servicemonitor.yaml b/helm/opengist/templates/servicemonitor.yaml new file mode 100644 index 0000000..9aa2dd6 --- /dev/null +++ b/helm/opengist/templates/servicemonitor.yaml @@ -0,0 +1,41 @@ +{{- if and (index .Values.config "metrics.enabled") .Values.service.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "opengist.fullname" . }} + namespace: {{ .Values.namespace | default .Release.Namespace }} + labels: + {{- include "opengist.labels" . | nindent 4 }} + {{- with .Values.service.metrics.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.metrics.serviceMonitor.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: metrics + {{- with .Values.service.metrics.serviceMonitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.service.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + path: /metrics + {{- with .Values.service.metrics.serviceMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.service.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Values.namespace | default .Release.Namespace }} + selector: + matchLabels: + {{- include "opengist.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: metrics +{{- end }} diff --git a/helm/opengist/templates/statefulset.yaml b/helm/opengist/templates/statefulset.yaml index 60034a6..ca3b4fe 100644 --- a/helm/opengist/templates/statefulset.yaml +++ b/helm/opengist/templates/statefulset.yaml @@ -140,6 +140,11 @@ spec: containerPort: {{ .Values.service.ssh.port }} protocol: TCP {{- end }} + {{- if index .Values.config "metrics.enabled" }} + - name: metrics + containerPort: {{ .Values.service.metrics.port }} + protocol: TCP + {{- end }} {{- if .Values.livenessProbe.enabled }} livenessProbe: {{- toYaml (omit .Values.livenessProbe "enabled") | nindent 12 }} @@ -172,7 +177,7 @@ spec: defaultMode: 511 - name: config-volume emptyDir: {} - {{- /* + {{- /* ======================================== VOLUME MOUNTING DECISION TREE ======================================== @@ -216,7 +221,7 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} - {{- /* + {{- /* ======================================== VOLUMECLAIMTEMPLATES DECISION TREE ======================================== @@ -224,14 +229,14 @@ spec: - persistence.enabled=true - persistence.existingClaim is empty - persistence.mode=perReplica (default) - + This creates one PVC per replica (RWO typically). - + NOT used when: - existingClaim is set (PVC already exists, referenced in volumes above) - mode=shared (standalone PVC created via pvc-shared.yaml) - persistence disabled (emptyDir used) - + WARNING: perReplica + replicaCount>1 causes data divergence. Use shared mode for multi-replica. */}} {{- if and .Values.persistence.enabled (ne (default "" .Values.persistence.existingClaim) "") }} diff --git a/helm/opengist/templates/svc-metrics.yaml b/helm/opengist/templates/svc-metrics.yaml new file mode 100644 index 0000000..4793bbf --- /dev/null +++ b/helm/opengist/templates/svc-metrics.yaml @@ -0,0 +1,32 @@ +{{- if index .Values.config "metrics.enabled" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "opengist.fullname" . }}-metrics + namespace: {{ .Values.namespace | default .Release.Namespace }} + labels: + {{- include "opengist.labels" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- with .Values.service.metrics.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.metrics.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.metrics.type }} + {{- if .Values.service.metrics.clusterIP }} + clusterIP: {{ .Values.service.metrics.clusterIP }} + {{- end }} + ports: + - port: {{ .Values.service.metrics.port }} + targetPort: metrics + protocol: TCP + name: metrics + {{- if and (eq .Values.service.metrics.type "NodePort") .Values.service.metrics.nodePort }} + nodePort: {{ .Values.service.metrics.nodePort }} + {{- end }} + selector: + {{- include "opengist.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/opengist/values.yaml b/helm/opengist/values.yaml index ac5eee4..826dbd1 100644 --- a/helm/opengist/values.yaml +++ b/helm/opengist/values.yaml @@ -8,6 +8,7 @@ namespace: "" config: log-level: "warn" log-output: "stdout" + metrics.enabled: false ## If defined, the existing secret will be used instead of creating a new one. ## The secret must contain a key named `config.yml` with the YAML configuration. @@ -101,6 +102,26 @@ service: loadBalancerSourceRanges: [] externalTrafficPolicy: + # A metrics K8S service on port 6158 is created when the Opengist config metrics.enabled: true + metrics: + type: ClusterIP + clusterIP: + port: 6158 + nodePort: + labels: {} + annotations: {} + + # A service monitor can be used to work with your Prometheus setup. + serviceMonitor: + enabled: true + labels: {} + # release: kube-prom-stack + interval: + scrapeTimeout: + annotations: {} + relabelings: [] + metricRelabelings: [] + ## HTTP Ingress for Opengist ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ ingress: diff --git a/internal/cli/main.go b/internal/cli/main.go index 3521297..a9192c4 100644 --- a/internal/cli/main.go +++ b/internal/cli/main.go @@ -9,6 +9,7 @@ import ( "github.com/thomiceli/opengist/internal/git" "github.com/thomiceli/opengist/internal/index" "github.com/thomiceli/opengist/internal/ssh" + "github.com/thomiceli/opengist/internal/web/handlers/metrics" "github.com/thomiceli/opengist/internal/web/server" "github.com/urfave/cli/v2" "os" @@ -36,12 +37,18 @@ var CmdStart = cli.Command{ Initialize(ctx) - server := server.NewServer(os.Getenv("OG_DEV") == "1", path.Join(config.GetHomeDir(), "sessions"), false) - go server.Start() + httpServer := server.NewServer(os.Getenv("OG_DEV") == "1", path.Join(config.GetHomeDir(), "sessions"), false) + go httpServer.Start() go ssh.Start() + var metricsServer *metrics.Server + if config.C.MetricsEnabled { + metricsServer = metrics.NewServer() + go metricsServer.Start() + } + <-stopCtx.Done() - shutdown(server) + shutdown(httpServer, metricsServer) return nil }, } @@ -131,7 +138,7 @@ func Initialize(ctx *cli.Context) { } } -func shutdown(server *server.Server) { +func shutdown(httpServer *server.Server, metricsServer *metrics.Server) { log.Info().Msg("Shutting down database...") if err := db.Close(); err != nil { log.Error().Err(err).Msg("Failed to close database") @@ -142,7 +149,11 @@ func shutdown(server *server.Server) { index.Close() } - server.Stop() + httpServer.Stop() + + if metricsServer != nil { + metricsServer.Stop() + } log.Info().Msg("Shutdown complete") } diff --git a/internal/config/config.go b/internal/config/config.go index 45dfb8b..78a4dd9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -79,7 +79,9 @@ type config struct { OIDCGroupClaimName string `yaml:"oidc.group-claim-name" env:"OG_OIDC_GROUP_CLAIM_NAME"` OIDCAdminGroup string `yaml:"oidc.admin-group" env:"OG_OIDC_ADMIN_GROUP"` - MetricsEnabled bool `yaml:"metrics.enabled" env:"OG_METRICS_ENABLED"` + MetricsEnabled bool `yaml:"metrics.enabled" env:"OG_METRICS_ENABLED"` + MetricsHost string `yaml:"metrics.host" env:"OG_METRICS_HOST"` + MetricsPort string `yaml:"metrics.port" env:"OG_METRICS_PORT"` LDAPUrl string `yaml:"ldap.url" env:"OG_LDAP_URL"` LDAPBindDn string `yaml:"ldap.bind-dn" env:"OG_LDAP_BIND_DN"` @@ -128,6 +130,8 @@ func configWithDefaults() (*config, error) { c.GiteaName = "Gitea" c.MetricsEnabled = false + c.MetricsHost = "0.0.0.0" + c.MetricsPort = "6158" return c, nil } diff --git a/internal/web/handlers/metrics/metrics.go b/internal/web/handlers/metrics/metrics.go index fdb57cc..06c30cb 100644 --- a/internal/web/handlers/metrics/metrics.go +++ b/internal/web/handlers/metrics/metrics.go @@ -1,16 +1,12 @@ package metrics import ( - "github.com/labstack/echo-contrib/echoprometheus" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/thomiceli/opengist/internal/config" "github.com/thomiceli/opengist/internal/db" - "github.com/thomiceli/opengist/internal/web/context" ) var ( - // Using promauto to automatically register metrics with the default registry countUsersGauge prometheus.Gauge countGistsGauge prometheus.Gauge countSSHKeysGauge prometheus.Gauge @@ -18,84 +14,52 @@ var ( metricsInitialized bool = false ) -// initMetrics initializes metrics if they're not already initialized func initMetrics() { if metricsInitialized { return } - // Only initialize metrics if they're enabled - if config.C.MetricsEnabled { - countUsersGauge = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "opengist_users_total", - Help: "Total number of users", - }, - ) + countUsersGauge = promauto.NewGauge( + prometheus.GaugeOpts{ + Name: "opengist_users_total", + Help: "Total number of users", + }, + ) - countGistsGauge = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "opengist_gists_total", - Help: "Total number of gists", - }, - ) + countGistsGauge = promauto.NewGauge( + prometheus.GaugeOpts{ + Name: "opengist_gists_total", + Help: "Total number of gists", + }, + ) - countSSHKeysGauge = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "opengist_ssh_keys_total", - Help: "Total number of SSH keys", - }, - ) + countSSHKeysGauge = promauto.NewGauge( + prometheus.GaugeOpts{ + Name: "opengist_ssh_keys_total", + Help: "Total number of SSH keys", + }, + ) - metricsInitialized = true - } + metricsInitialized = true } -// updateMetrics refreshes all metric values from the database func updateMetrics() { - // Only update metrics if they're enabled - if !config.C.MetricsEnabled || !metricsInitialized { + if !metricsInitialized { return } - // Update users count countUsers, err := db.CountAll(&db.User{}) if err == nil { countUsersGauge.Set(float64(countUsers)) } - // Update gists count countGists, err := db.CountAll(&db.Gist{}) if err == nil { countGistsGauge.Set(float64(countGists)) } - // Update SSH keys count countKeys, err := db.CountAll(&db.SSHKey{}) if err == nil { countSSHKeysGauge.Set(float64(countKeys)) } } - -// Metrics handles prometheus metrics endpoint requests. -func Metrics(ctx *context.Context) error { - // If metrics are disabled, return 404 - if !config.C.MetricsEnabled { - return ctx.NotFound("Metrics endpoint is disabled") - } - - // Initialize metrics if not already done - initMetrics() - - // Update metrics - updateMetrics() - - // Get the Echo context - echoCtx := ctx.Context - - // Use the Prometheus metrics handler - handler := echoprometheus.NewHandler() - - // Call the handler - return handler(echoCtx) -} diff --git a/internal/web/handlers/metrics/server.go b/internal/web/handlers/metrics/server.go new file mode 100644 index 0000000..ebd46a5 --- /dev/null +++ b/internal/web/handlers/metrics/server.go @@ -0,0 +1,50 @@ +package metrics + +import ( + "net/http" + + "github.com/labstack/echo-contrib/echoprometheus" + "github.com/labstack/echo/v4" + "github.com/rs/zerolog/log" + "github.com/thomiceli/opengist/internal/config" +) + +type Server struct { + echo *echo.Echo +} + +func NewServer() *Server { + e := echo.New() + e.HideBanner = true + e.HidePort = true + + s := &Server{echo: e} + + initMetrics() + + e.GET("/metrics", func(ctx echo.Context) error { + updateMetrics() + return echoprometheus.NewHandler()(ctx) + }) + + return s +} + +func (s *Server) Start() { + addr := config.C.MetricsHost + ":" + config.C.MetricsPort + log.Info().Msg("Starting metrics server on http://" + addr) + if err := s.echo.Start(addr); err != nil && err != http.ErrServerClosed { + log.Error().Err(err).Msg("Failed to start metrics server") + } +} + +func (s *Server) Stop() { + log.Info().Msg("Stopping metrics server...") + if err := s.echo.Close(); err != nil { + log.Error().Err(err).Msg("Failed to stop metrics server") + } +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.echo.ServeHTTP(w, r) +} diff --git a/internal/web/server/middlewares.go b/internal/web/server/middlewares.go index a703fff..834eed5 100644 --- a/internal/web/server/middlewares.go +++ b/internal/web/server/middlewares.go @@ -37,8 +37,7 @@ func (s *Server) registerMiddlewares() { s.echo.Use(Middleware(dataInit).toEcho()) s.echo.Use(Middleware(locale).toEcho()) if config.C.MetricsEnabled { - p := echoprometheus.NewMiddleware("opengist") - s.echo.Use(p) + s.echo.Use(echoprometheus.NewMiddleware("opengist")) } s.echo.Pre(middleware.MethodOverrideWithConfig(middleware.MethodOverrideConfig{ diff --git a/internal/web/server/router.go b/internal/web/server/router.go index 82d929a..6fc7c67 100644 --- a/internal/web/server/router.go +++ b/internal/web/server/router.go @@ -17,7 +17,6 @@ import ( "github.com/thomiceli/opengist/internal/web/handlers/gist" "github.com/thomiceli/opengist/internal/web/handlers/git" "github.com/thomiceli/opengist/internal/web/handlers/health" - "github.com/thomiceli/opengist/internal/web/handlers/metrics" "github.com/thomiceli/opengist/internal/web/handlers/settings" "github.com/thomiceli/opengist/public" ) @@ -34,10 +33,6 @@ func (s *Server) registerRoutes() { r.GET("/healthcheck", health.Healthcheck) - if config.C.MetricsEnabled { - r.GET("/metrics", metrics.Metrics) - } - r.GET("/register", auth.Register) r.POST("/register", auth.ProcessRegister) r.GET("/login", auth.Login) diff --git a/internal/web/test/metrics_test.go b/internal/web/test/metrics_test.go index 4fd2863..a425e79 100644 --- a/internal/web/test/metrics_test.go +++ b/internal/web/test/metrics_test.go @@ -1,15 +1,15 @@ package test import ( - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/thomiceli/opengist/internal/db" "io" - "net/http" - "os" + "net/http/httptest" "strconv" "strings" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/thomiceli/opengist/internal/db" ) var ( @@ -41,22 +41,12 @@ var ( // - Total number of SSH keys // // The test follows these steps: -// 1. Enables metrics via environment variable -// 2. Sets up test environment -// 3. Registers and logs in an admin user -// 4. Creates a gist and adds an SSH key -// 5. Queries the metrics endpoint -// 6. Verifies the reported metrics match expected values -// -// Environment variables: -// - OG_METRICS_ENABLED: Set to "true" for this test +// 1. Sets up test environment +// 2. Registers and logs in an admin user +// 3. Creates a gist and adds an SSH key +// 4. Creates a metrics server and queries the /metrics endpoint +// 5. Verifies the reported metrics match expected values func TestMetrics(t *testing.T) { - originalValue := os.Getenv("OG_METRICS_ENABLED") - - os.Setenv("OG_METRICS_ENABLED", "true") - - defer os.Setenv("OG_METRICS_ENABLED", originalValue) - s := Setup(t) defer Teardown(t, s) @@ -72,12 +62,16 @@ func TestMetrics(t *testing.T) { err = s.Request("POST", "/settings/ssh-keys", SSHKey, 302) require.NoError(t, err) - var metricsRes http.Response - err = s.Request("GET", "/metrics", nil, 200, &metricsRes) - require.NoError(t, err) + // Create a metrics server and query it + metricsServer := NewTestMetricsServer() - body, err := io.ReadAll(metricsRes.Body) - defer metricsRes.Body.Close() + req := httptest.NewRequest("GET", "/metrics", nil) + w := httptest.NewRecorder() + metricsServer.ServeHTTP(w, req) + + require.Equal(t, 200, w.Code) + + body, err := io.ReadAll(w.Body) require.NoError(t, err) lines := strings.Split(string(body), "\n") diff --git a/internal/web/test/server.go b/internal/web/test/server.go index a6926bb..7dfe9a6 100644 --- a/internal/web/test/server.go +++ b/internal/web/test/server.go @@ -22,6 +22,7 @@ import ( "github.com/thomiceli/opengist/internal/config" "github.com/thomiceli/opengist/internal/db" "github.com/thomiceli/opengist/internal/git" + "github.com/thomiceli/opengist/internal/web/handlers/metrics" "github.com/thomiceli/opengist/internal/web/server" ) @@ -240,3 +241,7 @@ type invitationAdmin struct { nbMax string `form:"nbMax"` expiredAtUnix string `form:"expiredAtUnix"` } + +func NewTestMetricsServer() *metrics.Server { + return metrics.NewServer() +}