From 5976e59468bfbfde17b7151295e47a22a16e7627 Mon Sep 17 00:00:00 2001 From: "d.y.gaevskiy" Date: Tue, 25 Aug 2020 18:31:42 +0300 Subject: [PATCH 1/2] metrics: implemented prometheus metrics manager (just like in ORY Hydra) and metrics endpoint --- .schema/api.swagger.json | 18 ++ cmd/daemon/serve.go | 4 + docs/docs/admin/managing-users-identities.mdx | 3 + docs/docs/reference/api.mdx | 189 ++++++++++++++++++ .../password-reset-recovery-link.mdx | 17 ++ driver/registry.go | 2 + driver/registry_default.go | 20 ++ go.mod | 3 +- go.sum | 9 + .../httpclient/client/admin/admin_client.go | 43 ++++ .../client/admin/prometheus_parameters.go | 112 +++++++++++ .../client/admin/prometheus_responses.go | 55 +++++ metrics/prometheus/handler.go | 59 ++++++ metrics/prometheus/metrics.go | 32 +++ metrics/prometheus/middleware.go | 24 +++ selfservice/form/html_form.go | 2 +- selfservice/strategy/oidc/form.go | 2 +- selfservice/strategy/password/registration.go | 2 +- selfservice/strategy/profile/strategy.go | 2 +- x/config.go | 2 +- 20 files changed, 594 insertions(+), 6 deletions(-) create mode 100644 internal/httpclient/client/admin/prometheus_parameters.go create mode 100644 internal/httpclient/client/admin/prometheus_responses.go create mode 100644 metrics/prometheus/handler.go create mode 100644 metrics/prometheus/metrics.go create mode 100644 metrics/prometheus/middleware.go diff --git a/.schema/api.swagger.json b/.schema/api.swagger.json index 9ff1138fe1e0..a68853a8bb9a 100755 --- a/.schema/api.swagger.json +++ b/.schema/api.swagger.json @@ -320,6 +320,24 @@ } } }, + "/metrics/prometheus": { + "get": { + "description": "```\nmetadata:\nannotations:\nprometheus.io/port: \"4445\"\nprometheus.io/path: \"/metrics/prometheus\"\n```", + "produces": [ + "plain/text" + ], + "tags": [ + "admin" + ], + "summary": "Get snapshot metrics from the Hydra service. If you're using k8s, you can then add annotations to\nyour deployment like so:", + "operationId": "prometheus", + "responses": { + "200": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." + } + } + } + }, "/recovery/link": { "post": { "description": "This endpoint creates a recovery link which should be given to the user in order for them to recover\n(or activate) their account.", diff --git a/cmd/daemon/serve.go b/cmd/daemon/serve.go index ef9ec298e121..5d759c2f07ff 100644 --- a/cmd/daemon/serve.go +++ b/cmd/daemon/serve.go @@ -5,6 +5,8 @@ import ( "strings" "sync" + "github.com/ory/kratos/metrics/prometheus" + "github.com/ory/analytics-go/v4" "github.com/ory/x/flagx" @@ -84,6 +86,7 @@ func serveAdmin(d driver.Driver, wg *sync.WaitGroup, cmd *cobra.Command, args [] r.RegisterAdminRoutes(router) n.Use(NewNegroniLoggerMiddleware(l, "admin#"+c.SelfAdminURL().String())) n.Use(sqa(cmd, d)) + n.Use(d.Registry().PrometheusManager()) if tracer := d.Registry().Tracer(); tracer.IsLoaded() { n.Use(tracer) @@ -144,6 +147,7 @@ func sqa(cmd *cobra.Command, d driver.Driver) *metricsx.Service { strings.ReplaceAll(verification.PublicVerificationInitPath, ":via", "email"), verification.PublicVerificationRequestPath, errorx.ErrorsPath, + prometheus.MetricsPrometheusPath, }, BuildVersion: d.Registry().BuildVersion(), BuildHash: d.Registry().BuildHash(), diff --git a/docs/docs/admin/managing-users-identities.mdx b/docs/docs/admin/managing-users-identities.mdx index 19ad5204364d..736195627d42 100644 --- a/docs/docs/admin/managing-users-identities.mdx +++ b/docs/docs/admin/managing-users-identities.mdx @@ -74,6 +74,7 @@ To create the account recovery link, use: ]}> + ```shell script $ curl --request POST -sL \ --header "Content-Type: application/json" \ @@ -93,6 +94,7 @@ $ curl --request POST -sL \ + ```go package main @@ -143,6 +145,7 @@ email, this feature is tracked as + ### Import a User Identity This feature is not implemented yet. diff --git a/docs/docs/reference/api.mdx b/docs/docs/reference/api.mdx index 5e6c460d2ce2..90d24bab3990 100644 --- a/docs/docs/reference/api.mdx +++ b/docs/docs/reference/api.mdx @@ -78,6 +78,7 @@ status will never refer to the cluster state, only to a single instance. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /health/alive \ -H 'Accept: application/json' @@ -86,6 +87,7 @@ curl -X GET /health/alive \ + ```go package main @@ -114,6 +116,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -134,6 +137,7 @@ fetch('/health/alive', { + ```java // This sample needs improvement. URL obj = new URL("/health/alive"); @@ -160,6 +164,7 @@ System.out.println(response.toString()); + ```python import requests @@ -178,6 +183,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -195,6 +201,7 @@ p JSON.parse(result) + ### Check readiness status @@ -244,6 +251,7 @@ status will never refer to the cluster state, only to a single instance. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /health/ready \ -H 'Accept: application/json' @@ -252,6 +260,7 @@ curl -X GET /health/ready \ + ```go package main @@ -280,6 +289,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -300,6 +310,7 @@ fetch('/health/ready', { + ```java // This sample needs improvement. URL obj = new URL("/health/ready"); @@ -326,6 +337,7 @@ System.out.println(response.toString()); + ```python import requests @@ -344,6 +356,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -361,6 +374,7 @@ p JSON.parse(result) + ## Administrative Endpoints @@ -457,6 +471,7 @@ Status Code **200** {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /identities \ -H 'Accept: application/json' @@ -465,6 +480,7 @@ curl -X GET /identities \ + ```go package main @@ -493,6 +509,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -513,6 +530,7 @@ fetch('/identities', { + ```java // This sample needs improvement. URL obj = new URL("/identities"); @@ -539,6 +557,7 @@ System.out.println(response.toString()); + ```python import requests @@ -557,6 +576,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -574,6 +594,7 @@ p JSON.parse(result) + ### Create an identity @@ -679,6 +700,7 @@ Learn how identities work in {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X POST /identities \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' @@ -687,6 +709,7 @@ curl -X POST /identities \ + ```go package main @@ -716,6 +739,7 @@ func main() { + ```javascript const fetch = require('node-fetch'); const input = '{ @@ -759,6 +783,7 @@ fetch('/identities', { + ```java // This sample needs improvement. URL obj = new URL("/identities"); @@ -785,6 +810,7 @@ System.out.println(response.toString()); + ```python import requests @@ -804,6 +830,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -822,6 +849,7 @@ p JSON.parse(result) + ### Get an identity @@ -894,6 +922,7 @@ Learn how identities work in {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /identities/{id} \ -H 'Accept: application/json' @@ -902,6 +931,7 @@ curl -X GET /identities/{id} \ + ```go package main @@ -930,6 +960,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -950,6 +981,7 @@ fetch('/identities/{id}', { + ```java // This sample needs improvement. URL obj = new URL("/identities/{id}"); @@ -976,6 +1008,7 @@ System.out.println(response.toString()); + ```python import requests @@ -994,6 +1027,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -1011,6 +1045,7 @@ p JSON.parse(result) + ### Update an identity @@ -1121,6 +1156,7 @@ Learn how identities work in {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X PUT /identities/{id} \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' @@ -1129,6 +1165,7 @@ curl -X PUT /identities/{id} \ + ```go package main @@ -1158,6 +1195,7 @@ func main() { + ```javascript const fetch = require('node-fetch'); const input = '{ @@ -1201,6 +1239,7 @@ fetch('/identities/{id}', { + ```java // This sample needs improvement. URL obj = new URL("/identities/{id}"); @@ -1227,6 +1266,7 @@ System.out.println(response.toString()); + ```python import requests @@ -1246,6 +1286,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -1264,6 +1305,7 @@ p JSON.parse(result) + ### Delete an identity @@ -1327,6 +1369,7 @@ Learn how identities work in {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X DELETE /identities/{id} \ -H 'Accept: application/json' @@ -1335,6 +1378,7 @@ curl -X DELETE /identities/{id} \ + ```go package main @@ -1363,6 +1407,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -1383,6 +1428,7 @@ fetch('/identities/{id}', { + ```java // This sample needs improvement. URL obj = new URL("/identities/{id}"); @@ -1409,6 +1455,7 @@ System.out.println(response.toString()); + ```python import requests @@ -1427,6 +1474,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -1444,6 +1492,7 @@ p JSON.parse(result) + ## common @@ -1500,6 +1549,7 @@ Get a traits schema definition {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /schemas/{id} \ -H 'Accept: application/json' @@ -1508,6 +1558,7 @@ curl -X GET /schemas/{id} \ + ```go package main @@ -1536,6 +1587,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -1556,6 +1608,7 @@ fetch('/schemas/{id}', { + ```java // This sample needs improvement. URL obj = new URL("/schemas/{id}"); @@ -1582,6 +1635,7 @@ System.out.println(response.toString()); + ```python import requests @@ -1600,6 +1654,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -1617,6 +1672,7 @@ p JSON.parse(result) + ### Get the request context of browser-based login user flows @@ -1802,6 +1858,7 @@ your application (e.g. `/login?request=abcde`). {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/requests/login?request=string \ -H 'Accept: application/json' @@ -1810,6 +1867,7 @@ curl -X GET /self-service/browser/flows/requests/login?request=string \ + ```go package main @@ -1838,6 +1896,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -1858,6 +1917,7 @@ fetch('/self-service/browser/flows/requests/login?request=string', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/requests/login?request=string"); @@ -1884,6 +1944,7 @@ System.out.println(response.toString()); + ```python import requests @@ -1903,6 +1964,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -1921,6 +1983,7 @@ p JSON.parse(result) + ### Get the request context of browser-based recovery flows @@ -2068,6 +2131,7 @@ your application (e.g. `/recover?request=abcde`). {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/requests/recovery?request=string \ -H 'Accept: application/json' @@ -2076,6 +2140,7 @@ curl -X GET /self-service/browser/flows/requests/recovery?request=string \ + ```go package main @@ -2104,6 +2169,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -2124,6 +2190,7 @@ fetch('/self-service/browser/flows/requests/recovery?request=string', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/requests/recovery?request=string"); @@ -2150,6 +2217,7 @@ System.out.println(response.toString()); + ```python import requests @@ -2169,6 +2237,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -2187,6 +2256,7 @@ p JSON.parse(result) + ### Get the request context of browser-based registration user flows @@ -2371,6 +2441,7 @@ your application (e.g. `/registration?request=abcde`). {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/requests/registration?request=string \ -H 'Accept: application/json' @@ -2379,6 +2450,7 @@ curl -X GET /self-service/browser/flows/requests/registration?request=string \ + ```go package main @@ -2407,6 +2479,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -2427,6 +2500,7 @@ fetch('/self-service/browser/flows/requests/registration?request=string', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/requests/registration?request=string"); @@ -2453,6 +2527,7 @@ System.out.println(response.toString()); + ```python import requests @@ -2472,6 +2547,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -2490,6 +2566,7 @@ p JSON.parse(result) + ### Get the request context of browser-based settings flows @@ -2660,6 +2737,7 @@ your application (e.g. `/settingss?request=abcde`). {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/requests/settings?request=string \ -H 'Accept: application/json' @@ -2668,6 +2746,7 @@ curl -X GET /self-service/browser/flows/requests/settings?request=string \ + ```go package main @@ -2696,6 +2775,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -2716,6 +2796,7 @@ fetch('/self-service/browser/flows/requests/settings?request=string', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/requests/settings?request=string"); @@ -2742,6 +2823,7 @@ System.out.println(response.toString()); + ```python import requests @@ -2761,6 +2843,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -2779,6 +2862,7 @@ p JSON.parse(result) + ### Get the request context of browser-based verification flows @@ -2887,6 +2971,7 @@ your application (e.g. `/verify?request=abcde`). {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/requests/verification?request=string \ -H 'Accept: application/json' @@ -2895,6 +2980,7 @@ curl -X GET /self-service/browser/flows/requests/verification?request=string \ + ```go package main @@ -2923,6 +3009,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -2943,6 +3030,7 @@ fetch('/self-service/browser/flows/requests/verification?request=string', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/requests/verification?request=string"); @@ -2969,6 +3057,7 @@ System.out.println(response.toString()); + ```python import requests @@ -2988,6 +3077,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3006,6 +3096,7 @@ p JSON.parse(result) + ### Get user-facing self-service errors @@ -3067,6 +3158,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/errors \ -H 'Accept: application/json' @@ -3075,6 +3167,7 @@ curl -X GET /self-service/errors \ + ```go package main @@ -3103,6 +3196,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -3123,6 +3217,7 @@ fetch('/self-service/errors', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/errors"); @@ -3149,6 +3244,7 @@ System.out.println(response.toString()); + ```python import requests @@ -3167,6 +3263,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3184,6 +3281,7 @@ p JSON.parse(result) + ## Public Endpoints @@ -3263,6 +3361,7 @@ to sign in again. This will reset the authenticated_at time of the session. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/login \ -H 'Accept: application/json' @@ -3271,6 +3370,7 @@ curl -X GET /self-service/browser/flows/login \ + ```go package main @@ -3299,6 +3399,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -3319,6 +3420,7 @@ fetch('/self-service/browser/flows/login', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/login"); @@ -3345,6 +3447,7 @@ System.out.println(response.toString()); + ```python import requests @@ -3363,6 +3466,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3380,6 +3484,7 @@ p JSON.parse(result) + ### Initialize Browser-Based Logout User Flow @@ -3440,6 +3545,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/logout \ -H 'Accept: application/json' @@ -3448,6 +3554,7 @@ curl -X GET /self-service/browser/flows/logout \ + ```go package main @@ -3476,6 +3583,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -3496,6 +3604,7 @@ fetch('/self-service/browser/flows/logout', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/logout"); @@ -3522,6 +3631,7 @@ System.out.println(response.toString()); + ```python import requests @@ -3540,6 +3650,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3557,6 +3668,7 @@ p JSON.parse(result) + ### Initialize browser-based account recovery flow @@ -3617,6 +3729,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/recovery \ -H 'Accept: application/json' @@ -3625,6 +3738,7 @@ curl -X GET /self-service/browser/flows/recovery \ + ```go package main @@ -3653,6 +3767,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -3673,6 +3788,7 @@ fetch('/self-service/browser/flows/recovery', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/recovery"); @@ -3699,6 +3815,7 @@ System.out.println(response.toString()); + ```python import requests @@ -3717,6 +3834,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3734,6 +3852,7 @@ p JSON.parse(result) + ### Complete the browser-based recovery flow using a recovery link @@ -3789,6 +3908,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X POST /self-service/browser/flows/recovery/link \ -H 'Accept: application/json' @@ -3797,6 +3917,7 @@ curl -X POST /self-service/browser/flows/recovery/link \ + ```go package main @@ -3825,6 +3946,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -3845,6 +3967,7 @@ fetch('/self-service/browser/flows/recovery/link', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/recovery/link"); @@ -3871,6 +3994,7 @@ System.out.println(response.toString()); + ```python import requests @@ -3889,6 +4013,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -3906,6 +4031,7 @@ p JSON.parse(result) + ### Initialize browser-based registration user flow @@ -3967,6 +4093,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/registration \ -H 'Accept: application/json' @@ -3975,6 +4102,7 @@ curl -X GET /self-service/browser/flows/registration \ + ```go package main @@ -4003,6 +4131,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -4023,6 +4152,7 @@ fetch('/self-service/browser/flows/registration', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/registration"); @@ -4049,6 +4179,7 @@ System.out.println(response.toString()); + ```python import requests @@ -4067,6 +4198,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -4084,6 +4216,7 @@ p JSON.parse(result) + ### Complete the browser-based settings flow for the OpenID Connect strategy @@ -4142,6 +4275,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X POST /self-service/browser/flows/registration/strategies/oidc/settings/connections \ -H 'Accept: application/json' @@ -4150,6 +4284,7 @@ curl -X POST /self-service/browser/flows/registration/strategies/oidc/settings/c + ```go package main @@ -4178,6 +4313,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -4201,6 +4337,7 @@ fetch( + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/registration/strategies/oidc/settings/connections"); @@ -4227,6 +4364,7 @@ System.out.println(response.toString()); + ```python import requests @@ -4245,6 +4383,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -4262,6 +4401,7 @@ p JSON.parse(result) + ### Initialize browser-based settings flow @@ -4322,6 +4462,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/settings \ -H 'Accept: application/json' @@ -4330,6 +4471,7 @@ curl -X GET /self-service/browser/flows/settings \ + ```go package main @@ -4358,6 +4500,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -4378,6 +4521,7 @@ fetch('/self-service/browser/flows/settings', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/settings"); @@ -4404,6 +4548,7 @@ System.out.println(response.toString()); + ```python import requests @@ -4422,6 +4567,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -4439,6 +4585,7 @@ p JSON.parse(result) + ### Complete the browser-based settings flow for the password strategy @@ -4497,6 +4644,7 @@ More information can be found at {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X POST /self-service/browser/flows/settings/strategies/password \ -H 'Accept: application/json' @@ -4505,6 +4653,7 @@ curl -X POST /self-service/browser/flows/settings/strategies/password \ + ```go package main @@ -4533,6 +4682,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -4553,6 +4703,7 @@ fetch('/self-service/browser/flows/settings/strategies/password', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/settings/strategies/password"); @@ -4579,6 +4730,7 @@ System.out.println(response.toString()); + ```python import requests @@ -4597,6 +4749,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -4614,6 +4767,7 @@ p JSON.parse(result) + ### Complete the browser-based settings flow for profile data @@ -4700,6 +4854,7 @@ traits: {} {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X POST /self-service/browser/flows/settings/strategies/profile?request=string \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' @@ -4708,6 +4863,7 @@ curl -X POST /self-service/browser/flows/settings/strategies/profile?request=str + ```go package main @@ -4737,6 +4893,7 @@ func main() { + ```javascript const fetch = require('node-fetch'); const input = '{ @@ -4761,6 +4918,7 @@ fetch('/self-service/browser/flows/settings/strategies/profile?request=string', + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/settings/strategies/profile?request=string"); @@ -4787,6 +4945,7 @@ System.out.println(response.toString()); + ```python import requests @@ -4807,6 +4966,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -4826,6 +4986,7 @@ p JSON.parse(result) + ### Initialize browser-based verification flow @@ -4900,6 +5061,7 @@ Currently only "email" is supported. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/verification/init/{via} \ -H 'Accept: application/json' @@ -4908,6 +5070,7 @@ curl -X GET /self-service/browser/flows/verification/init/{via} \ + ```go package main @@ -4936,6 +5099,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -4956,6 +5120,7 @@ fetch('/self-service/browser/flows/verification/init/{via}', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/verification/init/{via}"); @@ -4982,6 +5147,7 @@ System.out.println(response.toString()); + ```python import requests @@ -5000,6 +5166,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -5017,6 +5184,7 @@ p JSON.parse(result) + ### Complete the browser-based verification flows @@ -5089,6 +5257,7 @@ Currently only "email" is supported. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /self-service/browser/flows/verification/{via}/confirm/{code} \ -H 'Accept: application/json' @@ -5097,6 +5266,7 @@ curl -X GET /self-service/browser/flows/verification/{via}/confirm/{code} \ + ```go package main @@ -5125,6 +5295,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -5145,6 +5316,7 @@ fetch('/self-service/browser/flows/verification/{via}/confirm/{code}', { + ```java // This sample needs improvement. URL obj = new URL("/self-service/browser/flows/verification/{via}/confirm/{code}"); @@ -5171,6 +5343,7 @@ System.out.println(response.toString()); + ```python import requests @@ -5189,6 +5362,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -5206,6 +5380,7 @@ p JSON.parse(result) + ### Check who the current HTTP session belongs to @@ -5281,6 +5456,7 @@ This endpoint is useful for reverse proxies and API Gateways. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /sessions/whoami \ -H 'Accept: application/json' @@ -5289,6 +5465,7 @@ curl -X GET /sessions/whoami \ + ```go package main @@ -5317,6 +5494,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -5337,6 +5515,7 @@ fetch('/sessions/whoami', { + ```java // This sample needs improvement. URL obj = new URL("/sessions/whoami"); @@ -5363,6 +5542,7 @@ System.out.println(response.toString()); + ```python import requests @@ -5381,6 +5561,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -5398,6 +5579,7 @@ p JSON.parse(result) + ## version @@ -5450,6 +5632,7 @@ status will never refer to the cluster state, only to a single instance. {label: 'Java', value: 'java'}, {label: 'Python', value: 'python'}, {label: 'Ruby', value: 'ruby'}]}> + ```shell curl -X GET /version \ -H 'Accept: application/json' @@ -5458,6 +5641,7 @@ curl -X GET /version \ + ```go package main @@ -5486,6 +5670,7 @@ func main() { + ```javascript const fetch = require('node-fetch') @@ -5506,6 +5691,7 @@ fetch('/version', { + ```java // This sample needs improvement. URL obj = new URL("/version"); @@ -5532,6 +5718,7 @@ System.out.println(response.toString()); + ```python import requests @@ -5550,6 +5737,7 @@ print r.json() + ```ruby require 'rest-client' require 'json' @@ -5567,6 +5755,7 @@ p JSON.parse(result) + ## Schemas CredentialsType diff --git a/docs/docs/self-service/flows/account-recovery/password-reset-recovery-link.mdx b/docs/docs/self-service/flows/account-recovery/password-reset-recovery-link.mdx index c924e34d0d6c..942a1e7ff271 100644 --- a/docs/docs/self-service/flows/account-recovery/password-reset-recovery-link.mdx +++ b/docs/docs/self-service/flows/account-recovery/password-reset-recovery-link.mdx @@ -74,6 +74,7 @@ To initiate the request, point the browser to ]}> + ```html @@ -84,6 +85,7 @@ To initiate the request, point the browser to + ```js // ... window.location.href = @@ -93,6 +95,7 @@ window.location.href = + Next, the user is redirected to the Recovery UI set by config variable `selfservice.flows.recovery.ui_url` with a `?request=...` query parameter: @@ -105,6 +108,7 @@ Next, the user is redirected to the Recovery UI set by config variable ]}> + The browser is redirected to, for example: `http://127.0.0.1:4455/recovery?request=e219b0ee-58a8-4dc4-aeb6-294e9787dfa9` @@ -112,6 +116,7 @@ The browser is redirected to, for example: + ```json title="$ curl http:///self-service/browser/flows/requests/recovery?request=e219b0ee-58a8-4dc4-aeb6-294e9787dfa9" { "id": "e219b0ee-58a8-4dc4-aeb6-294e9787dfa9", @@ -148,6 +153,7 @@ The browser is redirected to, for example: + ```html

Recover your account

@@ -168,6 +174,7 @@ The browser is redirected to, for example: + The `state` parameter follows the state machine + ```json title="$ curl http:///self-service/browser/flows/requests/recovery?request=81d6f25e-6163-467a-afa3-1dae6c58b83d" { "id": "e6b25130-72d8-4776-8435-8d4790f7ec2f", @@ -255,6 +263,7 @@ set: + ```html

Recover your account

@@ -286,6 +295,7 @@ set: + If the form data is valid, the `state` is set to `sent_email` and `messages` will also be set: @@ -301,6 +311,7 @@ will also be set: + ```json title="$ curl http:///self-service/browser/flows/requests/recovery?request=7f3b531f-f78b-46ba-b770-873082dca1b7" { "id": "7f3b531f-f78b-46ba-b770-873082dca1b7", @@ -346,6 +357,7 @@ will also be set: + ```html

Recover your account

@@ -385,6 +397,7 @@ will also be set: + Once the user clicks the link in the E-Mail, she/he will be redirected to the Settings endpoint (e.g. `http://127.0.0.1:4455/settings?request=752b6d46-af3d-40d2-9d06-b3e3c0279f02`) @@ -402,6 +415,7 @@ directing the user to update the password / other credentials: + ```json5 title="$ curl http:///self-service/browser/flows/requests/settings?request=752b6d46-af3d-40d2-9d06-b3e3c0279f02" { id: '752b6d46-af3d-40d2-9d06-b3e3c0279f02', @@ -425,6 +439,7 @@ directing the user to update the password / other credentials: + If the user clicks an invalid (already used, expired) recovery link, a new recovery request will be initiated and she/he will be asked to retry the flow: @@ -439,6 +454,7 @@ recovery request will be initiated and she/he will be asked to retry the flow: + ```json5 title="$ curl http:///self-service/browser/flows/requests/recovery?request=ce7a8d78-ffd7-438f-90d3-f6b923faa405" { id: 'ce7a8d78-ffd7-438f-90d3-f6b923faa405', @@ -465,6 +481,7 @@ recovery request will be initiated and she/he will be asked to retry the flow: + ## API Clients API-based login and registration using this strategy will be addressed in a diff --git a/driver/registry.go b/driver/registry.go index 85ba3ef637a4..4997d6452d48 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -1,6 +1,7 @@ package driver import ( + "github.com/ory/kratos/metrics/prometheus" "github.com/ory/x/tracing" "github.com/gorilla/sessions" @@ -56,6 +57,7 @@ type Registry interface { RegisterRoutes(public *x.RouterPublic, admin *x.RouterAdmin) RegisterPublicRoutes(public *x.RouterPublic) RegisterAdminRoutes(admin *x.RouterAdmin) + PrometheusManager() *prometheus.MetricsManager Tracer() *tracing.Tracer x.CSRFProvider diff --git a/driver/registry_default.go b/driver/registry_default.go index 3abd13c03b0f..839dc201ce25 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/ory/kratos/metrics/prometheus" + "github.com/gobuffalo/pop/v5" "github.com/ory/kratos/continuity" @@ -65,8 +67,10 @@ type RegistryDefault struct { nosurf x.CSRFHandler trc *tracing.Tracer + pmm *prometheus.MetricsManager writer herodot.Writer healthxHandler *healthx.Handler + metricsHandler *prometheus.Handler courier *courier.Courier persister persistence.Persister @@ -180,6 +184,7 @@ func (m *RegistryDefault) RegisterAdminRoutes(router *x.RouterAdmin) { } m.HealthHandler().SetRoutes(router.Router, true) + m.MetricsHandler().SetRoutes(router.Router) } func (m *RegistryDefault) RegisterRoutes(public *x.RouterPublic, admin *x.RouterAdmin) { @@ -231,6 +236,14 @@ func (m *RegistryDefault) HealthHandler() *healthx.Handler { return m.healthxHandler } +func (m *RegistryDefault) MetricsHandler() *prometheus.Handler { + if m.metricsHandler == nil { + m.metricsHandler = prometheus.NewHandler(m.Writer(), m.BuildVersion()) + } + + return m.metricsHandler +} + func (m *RegistryDefault) WithCSRFHandler(c x.CSRFHandler) { m.nosurf = c } @@ -539,3 +552,10 @@ func (m *RegistryDefault) IdentityManager() *identity.Manager { } return m.identityManager } + +func (m *RegistryDefault) PrometheusManager() *prometheus.MetricsManager { + if m.pmm == nil { + m.pmm = prometheus.NewMetricsManager(m.buildVersion, m.buildHash, m.buildDate) + } + return m.pmm +} diff --git a/go.mod b/go.mod index 17e0b4d4f993..661d740f6086 100644 --- a/go.mod +++ b/go.mod @@ -62,9 +62,10 @@ require ( github.com/ory/jsonschema/v3 v3.0.1 github.com/ory/mail/v3 v3.0.0 github.com/ory/viper v1.7.5 - github.com/ory/x v0.0.135 + github.com/ory/x v0.0.146 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.4.0 github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 diff --git a/go.sum b/go.sum index 957dfc6c1cf2..add0cba69f96 100644 --- a/go.sum +++ b/go.sum @@ -82,6 +82,7 @@ github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -97,6 +98,7 @@ github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= @@ -956,6 +958,7 @@ github.com/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.5 h1:spfq8AyZ0cCk57Za6/juJ5btQxeE1FaEGMdfcI+XO48= github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= @@ -1084,6 +1087,8 @@ github.com/ory/x v0.0.128 h1:sArBGCH5s+0Zv0jD+t639Vy22URAD6XskBnD9r0+ESk= github.com/ory/x v0.0.128/go.mod h1:ykx1XOsl9taQtoW2yNvuxl/feEfTfrZTcbY1U7841tI= github.com/ory/x v0.0.135 h1:QjXkAI181EqrtMVQoYi0quFMOeu32B58HYbZPC1wFzg= github.com/ory/x v0.0.135/go.mod h1:BMzD4kJYW5/GHoBJndjO0lFy7igXz81UfpXzBQplscQ= +github.com/ory/x v0.0.146 h1:QCf1w0EJuC+P+4cNO5C6kZViQKMAO517d1iL16nquxc= +github.com/ory/x v0.0.146/go.mod h1:kYT14ajKGfDV8pRPRzQD3begYqRISZq1IQUgOVliQCE= github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -1117,18 +1122,22 @@ github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prY github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= diff --git a/internal/httpclient/client/admin/admin_client.go b/internal/httpclient/client/admin/admin_client.go index 558b19308123..a47919a2d15c 100644 --- a/internal/httpclient/client/admin/admin_client.go +++ b/internal/httpclient/client/admin/admin_client.go @@ -37,6 +37,8 @@ type ClientService interface { ListIdentities(params *ListIdentitiesParams) (*ListIdentitiesOK, error) + Prometheus(params *PrometheusParams) (*PrometheusOK, error) + UpdateIdentity(params *UpdateIdentityParams) (*UpdateIdentityOK, error) SetTransport(transport runtime.ClientTransport) @@ -232,6 +234,47 @@ func (a *Client) ListIdentities(params *ListIdentitiesParams) (*ListIdentitiesOK panic(msg) } +/* + Prometheus gets snapshot metrics from the hydra service if you re using k8s you can then add annotations to your deployment like so + + ``` +metadata: +annotations: +prometheus.io/port: "4445" +prometheus.io/path: "/metrics/prometheus" +``` +*/ +func (a *Client) Prometheus(params *PrometheusParams) (*PrometheusOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewPrometheusParams() + } + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "prometheus", + Method: "GET", + PathPattern: "/metrics/prometheus", + ProducesMediaTypes: []string{"plain/text"}, + ConsumesMediaTypes: []string{"application/json", "application/x-www-form-urlencoded"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &PrometheusReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + success, ok := result.(*PrometheusOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for prometheus: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* UpdateIdentity updates an identity diff --git a/internal/httpclient/client/admin/prometheus_parameters.go b/internal/httpclient/client/admin/prometheus_parameters.go new file mode 100644 index 000000000000..03b76358d868 --- /dev/null +++ b/internal/httpclient/client/admin/prometheus_parameters.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package admin + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewPrometheusParams creates a new PrometheusParams object +// with the default values initialized. +func NewPrometheusParams() *PrometheusParams { + + return &PrometheusParams{ + + timeout: cr.DefaultTimeout, + } +} + +// NewPrometheusParamsWithTimeout creates a new PrometheusParams object +// with the default values initialized, and the ability to set a timeout on a request +func NewPrometheusParamsWithTimeout(timeout time.Duration) *PrometheusParams { + + return &PrometheusParams{ + + timeout: timeout, + } +} + +// NewPrometheusParamsWithContext creates a new PrometheusParams object +// with the default values initialized, and the ability to set a context for a request +func NewPrometheusParamsWithContext(ctx context.Context) *PrometheusParams { + + return &PrometheusParams{ + + Context: ctx, + } +} + +// NewPrometheusParamsWithHTTPClient creates a new PrometheusParams object +// with the default values initialized, and the ability to set a custom HTTPClient for a request +func NewPrometheusParamsWithHTTPClient(client *http.Client) *PrometheusParams { + + return &PrometheusParams{ + HTTPClient: client, + } +} + +/*PrometheusParams contains all the parameters to send to the API endpoint +for the prometheus operation typically these are written to a http.Request +*/ +type PrometheusParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithTimeout adds the timeout to the prometheus params +func (o *PrometheusParams) WithTimeout(timeout time.Duration) *PrometheusParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the prometheus params +func (o *PrometheusParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the prometheus params +func (o *PrometheusParams) WithContext(ctx context.Context) *PrometheusParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the prometheus params +func (o *PrometheusParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the prometheus params +func (o *PrometheusParams) WithHTTPClient(client *http.Client) *PrometheusParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the prometheus params +func (o *PrometheusParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *PrometheusParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/internal/httpclient/client/admin/prometheus_responses.go b/internal/httpclient/client/admin/prometheus_responses.go new file mode 100644 index 000000000000..aee4fb32838a --- /dev/null +++ b/internal/httpclient/client/admin/prometheus_responses.go @@ -0,0 +1,55 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package admin + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// PrometheusReader is a Reader for the Prometheus structure. +type PrometheusReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *PrometheusReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewPrometheusOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewPrometheusOK creates a PrometheusOK with default headers values +func NewPrometheusOK() *PrometheusOK { + return &PrometheusOK{} +} + +/*PrometheusOK handles this case with default header values. + +Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201. +*/ +type PrometheusOK struct { +} + +func (o *PrometheusOK) Error() string { + return fmt.Sprintf("[GET /metrics/prometheus][%d] prometheusOK ", 200) +} + +func (o *PrometheusOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/metrics/prometheus/handler.go b/metrics/prometheus/handler.go new file mode 100644 index 000000000000..981d59e2126a --- /dev/null +++ b/metrics/prometheus/handler.go @@ -0,0 +1,59 @@ +package prometheus + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/ory/herodot" +) + +const ( + MetricsPrometheusPath = "/metrics/prometheus" +) + +// Handler handles HTTP requests to health and version endpoints. +type Handler struct { + H herodot.Writer + VersionString string +} + +// NewHandler instantiates a handler. +func NewHandler( + h herodot.Writer, + version string, +) *Handler { + return &Handler{ + H: h, + VersionString: version, + } +} + +// SetRoutes registers this handler's routes. +func (h *Handler) SetRoutes(r *httprouter.Router) { + r.GET(MetricsPrometheusPath, h.Metrics) +} + +// Metrics outputs prometheus metrics +// +// swagger:route GET /metrics/prometheus admin prometheus +// +// Get snapshot metrics from the Hydra service. If you're using k8s, you can then add annotations to +// your deployment like so: +// +// ``` +// metadata: +// annotations: +// prometheus.io/port: "4434" +// prometheus.io/path: "/metrics/prometheus" +// ``` +// +// Produces: +// - plain/text +// +// Responses: +// 200: emptyResponse +func (h *Handler) Metrics(rw http.ResponseWriter, r *http.Request, _ httprouter.Params) { + promhttp.Handler().ServeHTTP(rw, r) +} diff --git a/metrics/prometheus/metrics.go b/metrics/prometheus/metrics.go new file mode 100644 index 000000000000..5666f078d7ad --- /dev/null +++ b/metrics/prometheus/metrics.go @@ -0,0 +1,32 @@ +package prometheus + +import "github.com/prometheus/client_golang/prometheus" + +// Metrics prototypes +type Metrics struct { + ResponseTime *prometheus.HistogramVec +} + +// Method for creation new custom Prometheus metrics +func NewMetrics(version, hash, date string) *Metrics { + pm := &Metrics{ + ResponseTime: prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "kratos_response_time_seconds", + Help: "Description", + ConstLabels: map[string]string{ + "version": version, + "hash": hash, + "buildTime": date, + }, + }, + []string{"endpoint"}, + ), + } + err := prometheus.Register(pm.ResponseTime) + + if err != nil { + panic(err) + } + return pm +} diff --git a/metrics/prometheus/middleware.go b/metrics/prometheus/middleware.go new file mode 100644 index 000000000000..5c7f5cbae400 --- /dev/null +++ b/metrics/prometheus/middleware.go @@ -0,0 +1,24 @@ +package prometheus + +import ( + "net/http" + "time" +) + +type MetricsManager struct { + prometheusMetrics *Metrics +} + +func NewMetricsManager(version, hash, buildTime string) *MetricsManager { + return &MetricsManager{ + prometheusMetrics: NewMetrics(version, hash, buildTime), + } +} + +// Main middleware method to collect metrics for Prometheus. +func (pmm *MetricsManager) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + start := time.Now() + next(rw, r) + + pmm.prometheusMetrics.ResponseTime.WithLabelValues(r.URL.Path).Observe(time.Since(start).Seconds()) +} diff --git a/selfservice/form/html_form.go b/selfservice/form/html_form.go index 106809bbf9ef..6b6794c2e81b 100644 --- a/selfservice/form/html_form.go +++ b/selfservice/form/html_form.go @@ -70,7 +70,7 @@ func NewHTMLFormFromRequestBody(r *http.Request, action string, compiler decoder c := NewHTMLForm(action) raw := json.RawMessage(`{}`) if err := decoder.Decode(r, &raw, compiler, - decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnore), + decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnoreConversionErrors), ); err != nil { if err := c.ParseError(err); err != nil { return nil, err diff --git a/selfservice/strategy/oidc/form.go b/selfservice/strategy/oidc/form.go index edd23931a227..24df8c398e0c 100644 --- a/selfservice/strategy/oidc/form.go +++ b/selfservice/strategy/oidc/form.go @@ -56,7 +56,7 @@ func merge(userFormValues string, openIDProviderValues json.RawMessage, option d req, &df, decoderx.HTTPFormDecoder(), option, - decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnore), + decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnoreConversionErrors), decoderx.HTTPDecoderSetValidatePayloads(false), ); err != nil { return nil, err diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go index dd0a18c9fe46..72f02a5a4cd4 100644 --- a/selfservice/strategy/password/registration.go +++ b/selfservice/strategy/password/registration.go @@ -122,7 +122,7 @@ func (s *Strategy) handleRegistration(w http.ResponseWriter, r *http.Request, _ if err := decoderx.NewHTTP().Decode(r, &p, decoderx.HTTPFormDecoder(), option, - decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnore), + decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnoreConversionErrors), decoderx.HTTPDecoderSetValidatePayloads(false), ); err != nil { s.handleRegistrationError(w, r, ar, &p, err) diff --git a/selfservice/strategy/profile/strategy.go b/selfservice/strategy/profile/strategy.go index 123190d926b1..f8ab105652c0 100644 --- a/selfservice/strategy/profile/strategy.go +++ b/selfservice/strategy/profile/strategy.go @@ -169,7 +169,7 @@ func (s *Strategy) handleSubmit(w http.ResponseWriter, r *http.Request, ps httpr decoderx.HTTPFormDecoder(), option, decoderx.HTTPDecoderSetValidatePayloads(false), - decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnore), + decoderx.HTTPDecoderSetIgnoreParseErrorsStrategy(decoderx.ParseErrorIgnoreConversionErrors), ); err != nil { s.handleSettingsError(w, r, ctxUpdate, nil, &p, err) return diff --git a/x/config.go b/x/config.go index 3c6088f5dfa4..3703619a0e46 100644 --- a/x/config.go +++ b/x/config.go @@ -19,5 +19,5 @@ func WatchAndValidateViper(log *logrusx.Logger) { if err != nil { log.WithError(err).Fatal("Unable to read configuration JSON Schema.") } - viperx.WatchAndValidateViper(log, schema, "ORY Kratos", []string{"serve", "profiling", "log"}) + viperx.WatchAndValidateViper(log, schema, "ORY Kratos", []string{"serve", "profiling", "log"}, "") } From 0edd8a6547873b539369784bee70f39053ede7c1 Mon Sep 17 00:00:00 2001 From: "d.y.gaevskiy" Date: Wed, 26 Aug 2020 13:23:57 +0300 Subject: [PATCH 2/2] metrics: added test for prometheus handler --- go.mod | 1 + metrics/prometheus/handler_test.go | 32 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 metrics/prometheus/handler_test.go diff --git a/go.mod b/go.mod index 661d740f6086..d0b87ebf4cc2 100644 --- a/go.mod +++ b/go.mod @@ -66,6 +66,7 @@ require ( github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.4.0 + github.com/prometheus/common v0.9.1 github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 diff --git a/metrics/prometheus/handler_test.go b/metrics/prometheus/handler_test.go new file mode 100644 index 000000000000..f1209f30b075 --- /dev/null +++ b/metrics/prometheus/handler_test.go @@ -0,0 +1,32 @@ +package prometheus_test + +import ( + "github.com/ory/kratos/metrics/prometheus" + "github.com/prometheus/common/expfmt" + "github.com/stretchr/testify/require" + "net/http" + "net/http/httptest" + "testing" + + "github.com/ory/kratos/internal" + "github.com/ory/kratos/x" +) + +func TestHandler(t *testing.T) { + _, reg := internal.NewFastRegistryWithMocks(t) + router := x.NewRouterAdmin() + reg.MetricsHandler().SetRoutes(router.Router) + ts := httptest.NewServer(router) + defer ts.Close() + + c := http.DefaultClient + + response, err := c.Get(ts.URL + prometheus.MetricsPrometheusPath) + require.NoError(t, err) + require.EqualValues(t, http.StatusOK, response.StatusCode) + + textParser := expfmt.TextParser{} + text, err := textParser.TextToMetricFamilies(response.Body) + require.NoError(t, err) + require.EqualValues(t, "go_info", *text["go_info"].Name) +}