mirror of
https://github.com/thomiceli/opengist.git
synced 2025-06-13 13:57:13 +02:00
feat: add Prometheus metrics (#439)
* feat: add Prometheus metrics * setup metrics using Prometheus client under /metrics endpoint * add configuration value for metrics * configure Prometheus middleware for generic metrics * provide metrics for totals of users, gists and SSH keys * modify test request to optionally return the response * provide integration test for Prometheus metrics * update documentation * chore: make fmt
This commit is contained in:
@ -48,6 +48,9 @@ http.port: 6157
|
|||||||
# Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true
|
# Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true
|
||||||
http.git-enabled: true
|
http.git-enabled: true
|
||||||
|
|
||||||
|
# Enable or disable the metrics endpoint (either `true` or `false`). Default: false
|
||||||
|
metrics.enabled: false
|
||||||
|
|
||||||
# SSH built-in server configuration
|
# SSH built-in server configuration
|
||||||
# Note: it is not using the SSH daemon from your machine (yet)
|
# Note: it is not using the SSH daemon from your machine (yet)
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ export default defineConfig({
|
|||||||
{text: 'Custom assets', link: '/custom-assets'},
|
{text: 'Custom assets', link: '/custom-assets'},
|
||||||
{text: 'Custom links', link: '/custom-links'},
|
{text: 'Custom links', link: '/custom-links'},
|
||||||
{text: 'Cheat Sheet', link: '/cheat-sheet'},
|
{text: 'Cheat Sheet', link: '/cheat-sheet'},
|
||||||
|
{text: 'Metrics', link: '/metrics'},
|
||||||
{text: 'Admin panel', link: '/admin-panel'},
|
{text: 'Admin panel', link: '/admin-panel'},
|
||||||
], collapsed: false
|
], collapsed: false
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,7 @@ aside: false
|
|||||||
| http.host | OG_HTTP_HOST | `0.0.0.0` | The host on which the HTTP server should bind. |
|
| http.host | OG_HTTP_HOST | `0.0.0.0` | The host on which the HTTP server should bind. |
|
||||||
| http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. |
|
| 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`) |
|
| http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) |
|
||||||
|
| metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics endpoint at `/metrics` (`true` or `false`) |
|
||||||
| ssh.git-enabled | OG_SSH_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via SSH. (`true` or `false`) |
|
| 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.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. |
|
| ssh.port | OG_SSH_PORT | `2222` | The port on which the SSH server should listen. |
|
||||||
|
49
docs/configuration/metrics.md
Normal file
49
docs/configuration/metrics.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Metrics
|
||||||
|
|
||||||
|
Opengist offers built-in support for Prometheus metrics to help you monitor the performance and usage of your instance. These metrics provide insights into application health, user activity, and database statistics.
|
||||||
|
|
||||||
|
## 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):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metrics.enabled = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use the environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OG_METRICS_ENABLED=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Once enabled, metrics are available at the /metrics endpoint.
|
||||||
|
|
||||||
|
## Available metrics
|
||||||
|
|
||||||
|
### Opengist-specific metrics
|
||||||
|
|
||||||
|
| Metric Name | Type | Description |
|
||||||
|
|-------------|------|-------------|
|
||||||
|
| `opengist_users_total` | Gauge | Total number of registered users |
|
||||||
|
| `opengist_gists_total` | Gauge | Total number of gists in the system |
|
||||||
|
| `opengist_ssh_keys_total` | Gauge | Total number of SSH keys added by users |
|
||||||
|
|
||||||
|
### Standard HTTP metrics
|
||||||
|
|
||||||
|
In addition to the Opengist-specific metrics, standard Prometheus HTTP metrics are also available through the Echo Prometheus middleware. These include request durations, request counts, and request/response sizes.
|
||||||
|
|
||||||
|
These standard metrics follow the Prometheus naming convention and include labels for HTTP method, status code, and handler path.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
Example with Nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
location /metrics {
|
||||||
|
auth_basic "Metrics";
|
||||||
|
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||||
|
proxy_pass http://localhost:6157/metrics;
|
||||||
|
}
|
||||||
|
```
|
11
go.mod
11
go.mod
@ -14,9 +14,11 @@ require (
|
|||||||
github.com/gorilla/schema v1.4.1
|
github.com/gorilla/schema v1.4.1
|
||||||
github.com/gorilla/securecookie v1.1.2
|
github.com/gorilla/securecookie v1.1.2
|
||||||
github.com/gorilla/sessions v1.4.0
|
github.com/gorilla/sessions v1.4.0
|
||||||
|
github.com/labstack/echo-contrib v0.17.2
|
||||||
github.com/labstack/echo/v4 v4.13.3
|
github.com/labstack/echo/v4 v4.13.3
|
||||||
github.com/markbates/goth v1.80.0
|
github.com/markbates/goth v1.80.0
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/urfave/cli/v2 v2.27.5
|
github.com/urfave/cli/v2 v2.27.5
|
||||||
@ -35,6 +37,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/RoaringBitmap/roaring v1.9.4 // indirect
|
github.com/RoaringBitmap/roaring v1.9.4 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.17.0 // indirect
|
github.com/bits-and-blooms/bitset v1.17.0 // indirect
|
||||||
github.com/blevesearch/bleve_index_api v1.1.13 // indirect
|
github.com/blevesearch/bleve_index_api v1.1.13 // indirect
|
||||||
github.com/blevesearch/geo v0.1.20 // indirect
|
github.com/blevesearch/geo v0.1.20 // indirect
|
||||||
@ -54,6 +57,7 @@ require (
|
|||||||
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
|
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
|
||||||
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
|
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
|
||||||
github.com/boombuler/barcode v1.0.2 // indirect
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||||
@ -77,6 +81,7 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
@ -87,8 +92,12 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.61.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
@ -103,7 +112,7 @@ require (
|
|||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/time v0.8.0 // indirect
|
golang.org/x/time v0.8.0 // indirect
|
||||||
google.golang.org/protobuf v1.35.2 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
modernc.org/libc v1.61.2 // indirect
|
modernc.org/libc v1.61.2 // indirect
|
||||||
modernc.org/mathutil v1.6.0 // indirect
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
modernc.org/memory v1.8.0 // indirect
|
modernc.org/memory v1.8.0 // indirect
|
||||||
|
24
go.sum
24
go.sum
@ -12,6 +12,8 @@ github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eL
|
|||||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
|
github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
|
||||||
github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
@ -54,6 +56,8 @@ github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b/go.mod h1:
|
|||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
||||||
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9 h1:wMSvdj3BswqfQOXp2R1bJOAE7xIQLt2dlMQDMf836VY=
|
github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9 h1:wMSvdj3BswqfQOXp2R1bJOAE7xIQLt2dlMQDMf836VY=
|
||||||
github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||||
github.com/chromedp/chromedp v0.9.1 h1:CC7cC5p1BeLiiS2gfNNPwp3OaUxtRMBjfiw3E3k6dFA=
|
github.com/chromedp/chromedp v0.9.1 h1:CC7cC5p1BeLiiS2gfNNPwp3OaUxtRMBjfiw3E3k6dFA=
|
||||||
@ -148,10 +152,16 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
|||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/labstack/echo-contrib v0.17.2 h1:K1zivqmtcC70X9VdBFdLomjPDEVHlrcAObqmuFj1c6w=
|
||||||
|
github.com/labstack/echo-contrib v0.17.2/go.mod h1:NeDh3PX7j/u+jR4iuDt1zHmWZSCz9c/p9mxXcDpyS8E=
|
||||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
@ -179,6 +189,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -186,6 +198,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||||
|
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
@ -251,8 +271,8 @@ golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
|||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
@ -71,6 +71,8 @@ type config struct {
|
|||||||
OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"`
|
OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"`
|
||||||
OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"`
|
OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"`
|
||||||
|
|
||||||
|
MetricsEnabled bool `yaml:"metrics.enabled" env:"OG_METRICS_ENABLED"`
|
||||||
|
|
||||||
CustomName string `yaml:"custom.name" env:"OG_CUSTOM_NAME"`
|
CustomName string `yaml:"custom.name" env:"OG_CUSTOM_NAME"`
|
||||||
CustomLogo string `yaml:"custom.logo" env:"OG_CUSTOM_LOGO"`
|
CustomLogo string `yaml:"custom.logo" env:"OG_CUSTOM_LOGO"`
|
||||||
CustomFavicon string `yaml:"custom.favicon" env:"OG_CUSTOM_FAVICON"`
|
CustomFavicon string `yaml:"custom.favicon" env:"OG_CUSTOM_FAVICON"`
|
||||||
@ -110,6 +112,8 @@ func configWithDefaults() (*config, error) {
|
|||||||
c.GiteaUrl = "https://gitea.com"
|
c.GiteaUrl = "https://gitea.com"
|
||||||
c.GiteaName = "Gitea"
|
c.GiteaName = "Gitea"
|
||||||
|
|
||||||
|
c.MetricsEnabled = false
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package health
|
|
||||||
|
|
||||||
import "github.com/thomiceli/opengist/internal/web/context"
|
|
||||||
|
|
||||||
// Metrics is a dummy handler to satisfy the /metrics endpoint (for Prometheus, Openmetrics, etc.)
|
|
||||||
// until we have a proper metrics endpoint
|
|
||||||
func Metrics(ctx *context.Context) error {
|
|
||||||
return ctx.String(200, "")
|
|
||||||
}
|
|
101
internal/web/handlers/metrics/metrics.go
Normal file
101
internal/web/handlers/metrics/metrics.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
metricsInitialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateMetrics refreshes all metric values from the database
|
||||||
|
func updateMetrics() {
|
||||||
|
// Only update metrics if they're enabled
|
||||||
|
if !config.C.MetricsEnabled || !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)
|
||||||
|
}
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/labstack/echo-contrib/echoprometheus"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -34,6 +35,10 @@ func (s *Server) useCustomContext() {
|
|||||||
func (s *Server) registerMiddlewares() {
|
func (s *Server) registerMiddlewares() {
|
||||||
s.echo.Use(Middleware(dataInit).toEcho())
|
s.echo.Use(Middleware(dataInit).toEcho())
|
||||||
s.echo.Use(Middleware(locale).toEcho())
|
s.echo.Use(Middleware(locale).toEcho())
|
||||||
|
if config.C.MetricsEnabled {
|
||||||
|
p := echoprometheus.NewMiddleware("opengist")
|
||||||
|
s.echo.Use(p)
|
||||||
|
}
|
||||||
|
|
||||||
s.echo.Pre(middleware.MethodOverrideWithConfig(middleware.MethodOverrideConfig{
|
s.echo.Pre(middleware.MethodOverrideWithConfig(middleware.MethodOverrideConfig{
|
||||||
Getter: middleware.MethodFromForm("_method"),
|
Getter: middleware.MethodFromForm("_method"),
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/thomiceli/opengist/internal/config"
|
"github.com/thomiceli/opengist/internal/config"
|
||||||
"github.com/thomiceli/opengist/internal/index"
|
"github.com/thomiceli/opengist/internal/index"
|
||||||
@ -10,14 +17,9 @@ import (
|
|||||||
"github.com/thomiceli/opengist/internal/web/handlers/gist"
|
"github.com/thomiceli/opengist/internal/web/handlers/gist"
|
||||||
"github.com/thomiceli/opengist/internal/web/handlers/git"
|
"github.com/thomiceli/opengist/internal/web/handlers/git"
|
||||||
"github.com/thomiceli/opengist/internal/web/handlers/health"
|
"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/internal/web/handlers/settings"
|
||||||
"github.com/thomiceli/opengist/public"
|
"github.com/thomiceli/opengist/public"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) registerRoutes() {
|
func (s *Server) registerRoutes() {
|
||||||
@ -29,7 +31,10 @@ func (s *Server) registerRoutes() {
|
|||||||
r.POST("/preview", gist.Preview, logged)
|
r.POST("/preview", gist.Preview, logged)
|
||||||
|
|
||||||
r.GET("/healthcheck", health.Healthcheck)
|
r.GET("/healthcheck", health.Healthcheck)
|
||||||
r.GET("/metrics", health.Metrics)
|
|
||||||
|
if config.C.MetricsEnabled {
|
||||||
|
r.GET("/metrics", metrics.Metrics)
|
||||||
|
}
|
||||||
|
|
||||||
r.GET("/register", auth.Register)
|
r.GET("/register", auth.Register)
|
||||||
r.POST("/register", auth.ProcessRegister)
|
r.POST("/register", auth.ProcessRegister)
|
||||||
|
113
internal/web/test/metrics_test.go
Normal file
113
internal/web/test/metrics_test.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/thomiceli/opengist/internal/db"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SSHKey = db.SSHKeyDTO{
|
||||||
|
Title: "Test SSH Key",
|
||||||
|
Content: `ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q== admin@admin.local`,
|
||||||
|
}
|
||||||
|
AdminUser = db.UserDTO{
|
||||||
|
Username: "admin",
|
||||||
|
Password: "admin",
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleGist = db.GistDTO{
|
||||||
|
Title: "Simple Test Gist",
|
||||||
|
Description: "A simple gist for testing",
|
||||||
|
VisibilityDTO: db.VisibilityDTO{
|
||||||
|
Private: 0,
|
||||||
|
},
|
||||||
|
Name: []string{"file1.txt"},
|
||||||
|
Content: []string{"This is the content of file1"},
|
||||||
|
Topics: "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestMetrics tests the metrics endpoint functionality of the application.
|
||||||
|
// It verifies that the metrics endpoint correctly reports counts for:
|
||||||
|
// - Total number of users
|
||||||
|
// - Total number of gists
|
||||||
|
// - 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
|
||||||
|
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)
|
||||||
|
|
||||||
|
register(t, s, AdminUser)
|
||||||
|
login(t, s, AdminUser)
|
||||||
|
|
||||||
|
err := s.Request("GET", "/all", nil, 200)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.Request("POST", "/", SimpleGist, 302)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(metricsRes.Body)
|
||||||
|
defer metricsRes.Body.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
lines := strings.Split(string(body), "\n")
|
||||||
|
var usersTotal float64
|
||||||
|
var gistsTotal float64
|
||||||
|
var sshKeysTotal float64
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "opengist_users_total") {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
usersTotal, err = strconv.ParseFloat(parts[1], 64)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "opengist_gists_total") {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
gistsTotal, err = strconv.ParseFloat(parts[1], 64)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "opengist_ssh_keys_total") {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
sshKeysTotal, err = strconv.ParseFloat(parts[1], 64)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, 1.0, usersTotal, "opengist_users_total should be 1")
|
||||||
|
assert.Equal(t, 1.0, gistsTotal, "opengist_gists_total should be 1")
|
||||||
|
assert.Equal(t, 1.0, sshKeysTotal, "opengist_ssh_keys_total should be 1")
|
||||||
|
}
|
@ -48,7 +48,7 @@ func (s *TestServer) stop() {
|
|||||||
s.server.Stop()
|
s.server.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestServer) Request(method, uri string, data interface{}, expectedCode int) error {
|
func (s *TestServer) Request(method, uri string, data interface{}, expectedCode int, responsePtr ...*http.Response) error {
|
||||||
var bodyReader io.Reader
|
var bodyReader io.Reader
|
||||||
if method == http.MethodPost || method == http.MethodPut {
|
if method == http.MethodPost || method == http.MethodPut {
|
||||||
values := structToURLValues(data)
|
values := structToURLValues(data)
|
||||||
@ -92,6 +92,11 @@ func (s *TestServer) Request(method, uri string, data interface{}, expectedCode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a response pointer was provided, fill it with the response data
|
||||||
|
if len(responsePtr) > 0 && responsePtr[0] != nil {
|
||||||
|
*responsePtr[0] = *w.Result()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +162,7 @@ func Setup(t *testing.T) *TestServer {
|
|||||||
databaseType = os.Getenv("OPENGIST_TEST_DB")
|
databaseType = os.Getenv("OPENGIST_TEST_DB")
|
||||||
switch databaseType {
|
switch databaseType {
|
||||||
case "sqlite":
|
case "sqlite":
|
||||||
databaseDsn = "file:" + filepath.Join(homePath, "tmp", "opengist.db")
|
databaseDsn = "file:" + filepath.Join(homePath, "tmp", "opengist_test.db")
|
||||||
case "postgres":
|
case "postgres":
|
||||||
databaseDsn = "postgres://postgres:opengist@localhost:5432/opengist_test"
|
databaseDsn = "postgres://postgres:opengist@localhost:5432/opengist_test"
|
||||||
case "mysql":
|
case "mysql":
|
||||||
|
Reference in New Issue
Block a user