From 7d0aba7413c43fa3de11f9348b8b0e9b6ebcb99a Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 18 Jan 2024 14:58:57 +0000 Subject: [PATCH 01/26] fix(prism-agent): metrics for background jobs and metric naming Signed-off-by: David Poltorak --- .../atala/agent/server/PrismAgentApp.scala | 26 ++++++++++--------- .../server/jobs/IssueBackgroundJobs.scala | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/PrismAgentApp.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/PrismAgentApp.scala index 41fb335f3a..384ee58293 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/PrismAgentApp.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/PrismAgentApp.scala @@ -35,6 +35,8 @@ import io.iohk.atala.shared.utils.DurationOps.toMetricsSeconds import io.iohk.atala.system.controller.SystemServerEndpoints import zio.* import zio.metrics.* +import java.util.concurrent.Executors +import scala.concurrent.ExecutionContext object PrismAgentApp { @@ -58,11 +60,11 @@ object PrismAgentApp { ] = for { config <- ZIO.service[AppConfig] - _ <- IssueBackgroundJobs.issueCredentialDidCommExchanges - .repeat(Schedule.spaced(config.pollux.issueBgJobRecurrenceDelay)) - .unit @@ Metric + _ <- (IssueBackgroundJobs.issueCredentialDidCommExchanges @@ Metric .gauge("issuance_flow_did_com_exchange_job_ms_gauge") - .trackDurationWith(_.toMetricsSeconds) + .trackDurationWith(_.toMetricsSeconds)) + .repeat(Schedule.spaced(config.pollux.issueBgJobRecurrenceDelay)) + .unit } yield () private val presentProofExchangeJob: RIO[ @@ -72,11 +74,11 @@ object PrismAgentApp { ] = for { config <- ZIO.service[AppConfig] - _ <- PresentBackgroundJobs.presentProofExchanges - .repeat(Schedule.spaced(config.pollux.presentationBgJobRecurrenceDelay)) - .unit @@ Metric + _ <- (PresentBackgroundJobs.presentProofExchanges @@ Metric .gauge("present_proof_flow_did_com_exchange_job_ms_gauge") - .trackDurationWith(_.toMetricsSeconds) + .trackDurationWith(_.toMetricsSeconds)) + .repeat(Schedule.spaced(config.pollux.presentationBgJobRecurrenceDelay)) + .unit } yield () private val connectDidCommExchangesJob: RIO[ @@ -86,11 +88,11 @@ object PrismAgentApp { ] = for { config <- ZIO.service[AppConfig] - _ <- ConnectBackgroundJobs.didCommExchanges - .repeat(Schedule.spaced(config.connect.connectBgJobRecurrenceDelay)) - .unit @@ Metric + _ <- (ConnectBackgroundJobs.didCommExchanges @@ Metric .gauge("connection_flow_did_com_exchange_job_ms_gauge") - .trackDurationWith(_.toMetricsSeconds) + .trackDurationWith(_.toMetricsSeconds)) + .repeat(Schedule.spaced(config.connect.connectBgJobRecurrenceDelay)) + .unit } yield () private val syncDIDPublicationStateFromDltJob: URIO[ManagedDIDService & WalletManagementService, Unit] = diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala index e61ce931c1..3f1f6977e5 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala @@ -408,7 +408,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { @@ IssuerPendingToGeneratedFailed.trackError @@ IssuerPendingToGeneratedAll @@ Metric - .gauge("issuance_flow_issuer_cred_received_to_pending_flow_ms_gauge") + .gauge("issuance_flow_issuer_cred_pending_to_generated_flow_ms_gauge") .trackDurationWith(_.toMetricsSeconds) case IssueCredentialRecord( From b6780d0610707bd419dc2954831a50a892832800 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 18 Jan 2024 15:03:44 +0000 Subject: [PATCH 02/26] feat(prism-agent): add grafana dashboards, upgrade local perf testing Signed-off-by: David Poltorak --- .../single-tenant-testing-stack/.env | 2 +- .../dashboards/oea-k6-detail.json | 3288 ++++++++++ .../dashboards/oea-k8s-overview.json | 5609 +++++++++++++++++ .../docker-compose.yml | 78 +- .../run-performance-tests-local.sh | 14 +- 5 files changed, 8962 insertions(+), 29 deletions(-) create mode 100644 infrastructure/single-tenant-testing-stack/dashboards/oea-k6-detail.json create mode 100644 infrastructure/single-tenant-testing-stack/dashboards/oea-k8s-overview.json diff --git a/infrastructure/single-tenant-testing-stack/.env b/infrastructure/single-tenant-testing-stack/.env index a40f9fe1cb..3d851d1c25 100644 --- a/infrastructure/single-tenant-testing-stack/.env +++ b/infrastructure/single-tenant-testing-stack/.env @@ -1,3 +1,3 @@ -PRISM_AGENT_VERSION=1.17.0 +PRISM_AGENT_VERSION=1.25.0 PRISM_NODE_VERSION=2.2.1 VAULT_DEV_ROOT_TOKEN_ID=root diff --git a/infrastructure/single-tenant-testing-stack/dashboards/oea-k6-detail.json b/infrastructure/single-tenant-testing-stack/dashboards/oea-k6-detail.json new file mode 100644 index 0000000000..897c56f5f7 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/dashboards/oea-k6-detail.json @@ -0,0 +1,3288 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "enable": false, + "expr": "{compose_service=~\".*-oea\"} |= \"ERROR\"", + "iconColor": "red", + "instant": false, + "name": "Error in log" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 11, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 753, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "dark-red", + "index": 0, + "text": "Down" + }, + "1": { + "color": "green", + "index": 1, + "text": "Up" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 699, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(up{container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Agent `Up` Status", + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 726, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({compose_service=~\".*-oea\"}[$__interval])", + "legendFormat": "{{ compose_service }}", + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"prism-agent-server\", cluster=\"$cluster\"}[$__interval])", + "hide": false, + "legendFormat": "{{namespace}}", + "queryType": "range", + "refId": "B" + } + ], + "title": "[logs] lines logged", + "type": "timeseries" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 780, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({compose_service=~\".*-oea\"} |= \"ERROR\" [$__interval])", + "legendFormat": "{{ compose_service }}", + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\" [$__interval])", + "hide": false, + "legendFormat": "{{namespace}}", + "queryType": "range", + "refId": "B" + } + ], + "title": "[logs] error lines logged", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 13 + }, + "id": 807, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(connection_flow_inviter_process_connection_record_all_counter_total[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[invitee] connections completed per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 13 + }, + "id": 834, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(connection_flow_inviter_process_connection_record_all_counter_total[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[inviter] connections completed per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 13 + }, + "id": 861, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "(rate(issuance_flow_holder_req_generated_to_sent_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[holder] credentials requested per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 13 + }, + "id": 888, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(issuance_flow_issuer_send_cred_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[issuer] credentials generated per second (current)", + "type": "stat" + } + ], + "title": "Overview", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 21, + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 2 + }, + "id": 20, + "options": { + "dedupStrategy": "signature", + "enableLogDetails": true, + "prettifyLogMessage": true, + "showCommonLabels": false, + "showLabels": true, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{compose_service=~\".*-oea\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "B" + } + ], + "title": "Agent Errors", + "type": "logs" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 357, + "options": { + "dedupStrategy": "signature", + "enableLogDetails": true, + "prettifyLogMessage": true, + "showCommonLabels": false, + "showLabels": true, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{compose_service=~\".*apisix\"} |= \" 503 \"", + "hide": false, + "queryType": "range", + "refId": "B" + } + ], + "title": "APISIX Errors", + "type": "logs" + } + ], + "title": "Logs", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 2 + }, + "id": 889, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 0, + "y": 3 + }, + "id": 892, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "min" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Min - Iterations Per Second", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 5, + "y": 3 + }, + "id": 891, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Mean - Iterations Per Second", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 10, + "y": 3 + }, + "id": 890, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "max" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Max - Iterations Per Second", + "type": "stat" + } + ], + "title": "Scenario - Summary - $scenario_label", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 288, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": false, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "A" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "left" + }, + { + "id": "color", + "value": { + "fixedColor": "super-light-red", + "mode": "fixed" + } + }, + { + "id": "custom.axisLabel", + "value": "VUs" + }, + { + "id": "custom.scaleDistribution", + "value": { + "log": 10, + "type": "log" + } + }, + { + "id": "custom.showPoints", + "value": "never" + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "B" + }, + "properties": [ + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + }, + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + }, + { + "id": "custom.axisLabel", + "value": "RPS" + }, + { + "id": "custom.lineWidth", + "value": 3 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "E" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + }, + { + "id": "custom.lineWidth", + "value": 5 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "C" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + }, + { + "id": "unit", + "value": "s" + }, + { + "id": "custom.fillOpacity", + "value": 18 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "custom.axisLabel", + "value": "Response Time" + }, + { + "id": "decimals", + "value": 2 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Iteration failure percentage due to poll timeout" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "hidden" + }, + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "min", + "value": 0 + }, + { + "id": "max", + "value": 1 + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.lineWidth", + "value": 7 + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "solid" + } + }, + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Iteration Duration" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Iteration Rate" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "light-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 22, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 287, + "interval": "5", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "timezones": [ + "browser" + ], + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "k6_vus", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "VUs", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\"}[$__rate_interval]))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "Request Rate", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\", expected_response=\"false\"}[$__rate_interval]))", + "hide": true, + "instant": false, + "legendFormat": "Failed Requests Rate ", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(histogram_sum(rate(k6_group_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))\n/\nhistogram_count(rate(k6_group_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))) ", + "hide": false, + "legendFormat": "{{group}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\", expected_response=\"false\"}[$__rate_interval])) / sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\"}[$__rate_interval])) * 100", + "hide": false, + "instant": false, + "legendFormat": "Failed Requests Percentage Rate", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(histogram_sum(rate(k6_iteration_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))\n/\nhistogram_count(rate(k6_iteration_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))) ", + "hide": false, + "instant": false, + "legendFormat": "Iteration Duration", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": " sum(rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Iteration Rate", + "range": true, + "refId": "G" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(k6_status_change_timeouts_total{scenario_label=\"$scenario_label\"}[$__rate_interval])) / sum(rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Iteration failure percentage due to poll timeout", + "range": true, + "refId": "I" + } + ], + "title": "[k6] scenario group duration ($scenario_label)", + "type": "timeseries" + } + ], + "repeat": "scenario_label", + "repeatDirection": "h", + "title": " Scenario - Detail - $scenario_label", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 11, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "min", + "lastNotNull", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(jvm_memory_bytes_used{area=~\"$memarea\",container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "hide": false, + "interval": "", + "legendFormat": "Used - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(jvm_memory_bytes_max{area=~\"$memarea\",container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "hide": false, + "interval": "", + "legendFormat": "Max - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "JVM Memory Used [$memarea]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 13, + "links": [], + "options": { + "legend": { + "calcs": [ + "min", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(jvm_gc_collection_seconds_sum{container=\"prism-agent-server\", cluster=\"${cluster}\"}[$__rate_interval])) by (gc, namespace)", + "format": "time_series", + "interval": "60s", + "intervalFactor": 1, + "legendFormat": "{{gc}} - {{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "GC Time rate", + "type": "timeseries" + } + ], + "title": "JVM Metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 8, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "quota - requests" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#F2495C", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "quota - limits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FF9830", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 4, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{cluster=\"$cluster\", container=\"prism-agent-server\"}) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "quota - requests" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#F2495C", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "quota - limits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FF9830", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 5, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(container_memory_working_set_bytes{job!=\"\", cluster=\"$cluster\", container=\"prism-agent-server\", image!=\"\"}) by (namespace)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Usage (w/o cache)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 6, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(container_network_receive_bytes_total{cluster=\"$cluster\", pod=~\"prism-agent-server.*\"}[$__rate_interval])) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Receive Bandwidth", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 7, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(container_network_transmit_bytes_total{cluster=\"$cluster\", pod=~\"prism-agent-server.*\"}[$__rate_interval])) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Transmit Bandwidth", + "type": "timeseries" + } + ], + "title": "Container Metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 14, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.width" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "100": { + "color": "super-light-purple", + "index": 0, + "text": "100 Continue" + }, + "101": { + "color": "super-light-purple", + "index": 1, + "text": "101 Switching Protocols" + }, + "102": { + "color": "super-light-purple", + "index": 2, + "text": "102 Processing" + }, + "103": { + "color": "super-light-purple", + "index": 3, + "text": "103 Early Hints" + }, + "200": { + "color": "blue", + "index": 4, + "text": "200 OK" + }, + "201": { + "color": "blue", + "index": 5, + "text": "201 Created" + }, + "202": { + "color": "blue", + "index": 6, + "text": "202 Accepted" + }, + "203": { + "color": "blue", + "index": 7, + "text": "203 Non-Authoritative Information" + }, + "204": { + "color": "blue", + "index": 8, + "text": "204 No Content" + }, + "205": { + "color": "blue", + "index": 9, + "text": "205 Reset Content" + }, + "206": { + "color": "blue", + "index": 10, + "text": "206 Partial Content" + }, + "207": { + "color": "blue", + "index": 11, + "text": "207 Multi-Status" + }, + "208": { + "color": "blue", + "index": 12, + "text": "208 Already Reported " + }, + "226": { + "color": "blue", + "index": 13, + "text": "226 IM Used " + }, + "300": { + "color": "purple", + "index": 14, + "text": "300 Multiple Choices" + }, + "301": { + "color": "purple", + "index": 15, + "text": "301 Moved Permanently" + }, + "302": { + "color": "purple", + "index": 16, + "text": "302 Found" + }, + "303": { + "color": "purple", + "index": 17, + "text": "303 See Other" + }, + "304": { + "color": "purple", + "index": 18, + "text": "304 Not Modified" + }, + "305": { + "color": "purple", + "index": 19, + "text": "305 Use Proxy " + }, + "307": { + "color": "purple", + "index": 20, + "text": "307 Temporary Redirect" + }, + "308": { + "color": "purple", + "index": 21, + "text": "308 Permanent Redirect" + }, + "400": { + "color": "orange", + "index": 22, + "text": "400 Bad Request" + }, + "401": { + "color": "orange", + "index": 23, + "text": "401 Unauthorized" + }, + "402": { + "color": "orange", + "index": 24, + "text": "402 Payment Required" + }, + "403": { + "color": "orange", + "index": 25, + "text": "403 Forbidden" + }, + "404": { + "color": "orange", + "index": 26, + "text": "404 Not Found" + }, + "405": { + "color": "orange", + "index": 27, + "text": "405 Method Not Allowed" + }, + "406": { + "color": "orange", + "index": 28, + "text": "406 Not Acceptable" + }, + "407": { + "color": "orange", + "index": 29, + "text": "407 Proxy Authentication Required" + }, + "408": { + "color": "orange", + "index": 30, + "text": "408 Request Timeout" + }, + "409": { + "color": "orange", + "index": 31, + "text": "409 Conflict" + }, + "410": { + "color": "orange", + "index": 32, + "text": "410 Gone" + }, + "411": { + "color": "orange", + "index": 33, + "text": "411 Length Required" + }, + "412": { + "color": "orange", + "index": 34, + "text": "412 Precondition Failed" + }, + "413": { + "color": "orange", + "index": 35, + "text": "413 Payload Too Large" + }, + "414": { + "color": "orange", + "index": 36, + "text": "414 URI Too Long" + }, + "415": { + "color": "orange", + "index": 37, + "text": "415 Unsupported Media Type" + }, + "416": { + "color": "orange", + "index": 38, + "text": "416 Range Not Satisfiable" + }, + "417": { + "color": "orange", + "index": 39, + "text": "417 Expectation Failed" + }, + "418": { + "color": "orange", + "index": 40, + "text": "418 I'm a teapot" + }, + "421": { + "color": "orange", + "index": 41, + "text": "421 Misdirected Request" + }, + "422": { + "color": "orange", + "index": 42, + "text": "422 Unprocessable Entity" + }, + "423": { + "color": "orange", + "index": 43, + "text": "423 Locked " + }, + "424": { + "color": "orange", + "index": 44, + "text": "424 Failed Dependency" + }, + "425": { + "color": "orange", + "index": 45, + "text": "425 Too Early" + }, + "426": { + "color": "orange", + "index": 46, + "text": "426 Upgrade Required" + }, + "428": { + "color": "orange", + "index": 47, + "text": "428 Precondition Required" + }, + "429": { + "color": "orange", + "index": 48, + "text": "429 Too Many Requests" + }, + "431": { + "color": "orange", + "index": 49, + "text": "431 Request Header Fields Too Large" + }, + "451": { + "color": "orange", + "index": 50, + "text": "451 Unavailable For Legal Reasons431 Request Header Fields Too Large" + }, + "500": { + "color": "red", + "index": 51, + "text": "500 Internal Server Error" + }, + "501": { + "color": "red", + "index": 52, + "text": "501 Not Implemented" + }, + "502": { + "color": "red", + "index": 53, + "text": "502 Bad Gateway" + }, + "503": { + "color": "red", + "index": 54, + "text": "503 Service Unavailable" + }, + "504": { + "color": "red", + "index": 55, + "text": "504 Gateway Timeout" + }, + "505": { + "color": "red", + "index": 56, + "text": "505 HTTP Version Not Supported" + }, + "506": { + "color": "red", + "index": 57, + "text": "506 Variant Also Negotiates" + }, + "507": { + "color": "red", + "index": 58, + "text": "507 Insufficient Storage" + }, + "508": { + "color": "red", + "index": 59, + "text": "508 Loop Detected " + }, + "510": { + "color": "red", + "index": 60, + "text": "510 Not Extended" + }, + "511": { + "color": "red", + "index": 61, + "text": "511 Network Authentication Required" + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "count" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + }, + { + "id": "custom.width", + "value": 81 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "min " + }, + "properties": [ + { + "id": "unit", + "value": "s" + }, + { + "id": "custom.width", + "value": 101 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "mean" + }, + "properties": [ + { + "id": "unit", + "value": "s" + }, + { + "id": "custom.width", + "value": 97 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "max" + }, + "properties": [ + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "p95" + }, + "properties": [ + { + "id": "unit", + "value": "s" + }, + { + "id": "custom.width", + "value": 92 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "testid" + }, + "properties": [ + { + "id": "custom.width", + "value": 132 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "custom.width" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "scenario" + }, + "properties": [ + { + "id": "custom.width", + "value": 283 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "method" + }, + "properties": [ + { + "id": "custom.width", + "value": 90 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "testid" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "match": "empty", + "result": { + "index": 0, + "text": "without testid" + } + }, + "type": "special" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 894, + "interval": "1", + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "k6_http_reqs_total{scenario_label=\"$scenario_label\"}", + "format": "table", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0, sum by(group) (rate(k6_http_req_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval])))", + "format": "table", + "hide": false, + "legendFormat": "", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum by(group) (rate(k6_http_req_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval])))", + "format": "table", + "hide": false, + "legendFormat": "", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(group) (rate(k6_http_req_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval])))", + "format": "table", + "hide": false, + "legendFormat": "", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(1, sum by(group) (rate(k6_http_req_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval])))", + "format": "table", + "hide": false, + "legendFormat": "", + "range": true, + "refId": "B" + } + ], + "title": "Requests by URL", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [ + "min", + "max" + ], + "operation": "aggregate" + }, + "Value #A": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #B": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #C": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #D": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #E": { + "aggregations": [], + "operation": "aggregate" + }, + "Value #F": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "method": { + "aggregations": [], + "operation": "groupby" + }, + "name": { + "aggregations": [], + "operation": "groupby" + }, + "scenario": { + "aggregations": [], + "operation": "groupby" + }, + "status": { + "aggregations": [], + "operation": "groupby" + }, + "testid": { + "aggregations": [], + "operation": "groupby" + }, + "tls_version": { + "aggregations": [] + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A (lastNotNull)": 5, + "Value #C (lastNotNull)": 6, + "Value #D (lastNotNull)": 7, + "method": 3, + "name": 1, + "scenario": 2, + "status": 4, + "testid": 0 + }, + "renameByName": { + "Value": "", + "Value #A (lastNotNull)": "count", + "Value #B (lastNotNull)": "max", + "Value #B (min)": "min", + "Value #C (lastNotNull)": "min ", + "Value #C (mean)": "", + "Value #D (lastNotNull)": "mean", + "Value #E (max)": "max", + "Value #F (lastNotNull)": "p95" + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "isNull", + "options": {} + }, + "fieldName": "count" + } + ], + "match": "any", + "type": "exclude" + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "#6ED0E0", + "value": 0.9 + }, + { + "color": "blue", + "value": 1 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Success Rate" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + }, + { + "id": "color", + "value": { + "mode": "thresholds" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 895, + "interval": "1", + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": [], + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 2, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "k6_checks_rate{scenario_label=\"$scenario_label\"}", + "format": "table", + "instant": false, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Checks", + "transformations": [ + { + "id": "labelsToFields", + "options": { + "mode": "columns" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #A": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #B": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #C": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #D": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #E": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #F": { + "aggregations": [] + }, + "__name__": { + "aggregations": [] + }, + "check": { + "aggregations": [], + "operation": "groupby" + }, + "expected_response": { + "aggregations": [], + "operation": "groupby" + }, + "scenario": { + "aggregations": [], + "operation": "groupby" + }, + "test_type": { + "aggregations": [] + }, + "testid": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Value (last)": false, + "testid": false + }, + "indexByName": { + "Value (lastNotNull)": 3, + "check": 1, + "scenario": 2, + "testid": 0 + }, + "renameByName": { + "Value #A (lastNotNull)": "P95 Response Time", + "Value #B (lastNotNull)": "Failed Request Count", + "Value #C (lastNotNull)": "AVG RPS", + "Value #D (lastNotNull)": "Iterations", + "Value #E (lastNotNull)": "Request Count", + "Value (last)": "Success Rate", + "Value (lastNotNull)": "Success Rate", + "scenario": "Scenario" + } + } + } + ], + "type": "table" + } + ], + "title": "Incubating", + "type": "row" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "local-cluster", + "value": "local-cluster" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(cluster)", + "hide": 0, + "includeAll": false, + "label": "Cluster", + "multi": false, + "name": "cluster", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(cluster)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(jvm_memory_bytes_used{cluster=\"$cluster\", container=\"prism-agent-server\"},area)", + "hide": 2, + "includeAll": true, + "multi": true, + "name": "memarea", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(jvm_memory_bytes_used{cluster=\"$cluster\", container=\"prism-agent-server\"},area)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "grafanacloud-prom", + "value": "PA396B7035A9690C9" + }, + "hide": 0, + "includeAll": false, + "label": "Prometheus Datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "grafanacloud-logs", + "value": "P542D2CD8B7CD28BD" + }, + "hide": 0, + "includeAll": false, + "label": "Loki Datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "st-connection-flow-smoke", + "value": "st-connection-flow-smoke" + }, + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "definition": "label_values(scenario_label)", + "hide": 0, + "includeAll": false, + "label": "Scenario Label", + "multi": false, + "name": "scenario_label", + "options": [], + "query": { + "query": "label_values(scenario_label)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Open Enterprise Agent - Performance Testing Detail", + "uid": "oea-k6-detail", + "version": 10, + "weekStart": "" +} \ No newline at end of file diff --git a/infrastructure/single-tenant-testing-stack/dashboards/oea-k8s-overview.json b/infrastructure/single-tenant-testing-stack/dashboards/oea-k8s-overview.json new file mode 100644 index 0000000000..7cc0c5125c --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/dashboards/oea-k8s-overview.json @@ -0,0 +1,5609 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "enable": false, + "expr": "{compose_service=~\".*-oea\"} |= \"ERROR\"", + "iconColor": "red", + "instant": false, + "name": "Error in log" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 10, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 753, + "panels": [], + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "dark-red", + "index": 0, + "text": "Down" + }, + "1": { + "color": "green", + "index": 1, + "text": "Up" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 699, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(up{container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Agent `Up` Status", + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 726, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({compose_service=~\".*-oea\"}[$__interval])", + "legendFormat": "{{ compose_service }}", + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"prism-agent-server\", cluster=\"$cluster\"}[$__interval])", + "hide": false, + "legendFormat": "{{namespace}}", + "queryType": "range", + "refId": "B" + } + ], + "title": "[logs] lines logged", + "type": "timeseries" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 780, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({compose_service=~\".*-oea\"} |= \"ERROR\" [$__interval])", + "legendFormat": "{{ compose_service }}", + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\" [$__interval])", + "hide": false, + "legendFormat": "{{namespace}}", + "queryType": "range", + "refId": "B" + } + ], + "title": "[logs] error lines logged", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 13 + }, + "id": 807, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(connection_flow_inviter_process_connection_record_all_counter_total[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[invitee] connections completed per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 13 + }, + "id": 834, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(connection_flow_inviter_process_connection_record_all_counter_total[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[inviter] connections completed per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 13 + }, + "id": 861, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "(rate(issuance_flow_holder_req_generated_to_sent_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[holder] credentials requested per second (current)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 13 + }, + "id": 888, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(issuance_flow_issuer_send_cred_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "[issuer] credentials generated per second (current)", + "type": "stat" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 9, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 70, + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "mappings": [ + { + "options": { + "0": { + "color": "dark-red", + "index": 0, + "text": "Down" + }, + "1": { + "color": "green", + "index": 1, + "text": "Up" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colWidth": 0.9, + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "rowHeight": 0.9, + "showValue": "auto", + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(up{container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "format": "time_series", + "instant": false, + "interval": "150", + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "Agent `Up` History", + "type": "status-history" + } + ], + "title": "Status History", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 21, + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 20, + "options": { + "dedupStrategy": "signature", + "enableLogDetails": true, + "prettifyLogMessage": true, + "showCommonLabels": false, + "showLabels": true, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{compose_service=~\".*-oea\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "B" + } + ], + "title": "Agent Errors", + "type": "logs" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 357, + "options": { + "dedupStrategy": "signature", + "enableLogDetails": true, + "prettifyLogMessage": true, + "showCommonLabels": false, + "showLabels": true, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{container=\"prism-agent-server\", cluster=\"$cluster\"} |= \"ERROR\"", + "hide": false, + "queryType": "range", + "refId": "A" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "editorMode": "code", + "expr": "{compose_service=~\".*apisix\"} |= \" 503 \"", + "hide": false, + "queryType": "range", + "refId": "B" + } + ], + "title": "APISIX Errors", + "type": "logs" + } + ], + "title": "Logs", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 251, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 269, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_did_com_exchange_job_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] background job processing (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 374, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_invitee_process_connection_record_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "Invitee - Connection response pending - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_inviter_process_connection_record_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Inviter - Connection response pending - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "[connection] background job total records processed (rate)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 29 + }, + "id": 421, + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "issuance_flow_issuer_send_offer_flow_ms_gauge", + "instant": false, + "legendFormat": "HTPT Request Duration - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "issuance_flow_issuer_send_offer_ms_gauge", + "hide": false, + "instant": false, + "legendFormat": "Send Offer Processing Time - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "[connection] send offer http request versus send flow (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 29 + }, + "id": 404, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_invitee_process_connection_record_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "Invitee - Connection response pending - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_inviter_process_connection_record_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "hide": false, + "instant": false, + "legendFormat": "Inviter - Connection response pending - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "[connection] background job total records processed (count)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 359, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "connection_flow_invitee_process_connection_record_ms_gauge", + "instant": false, + "legendFormat": "Invitee - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "connection_flow_inviter_process_connection_record_ms_gauge", + "hide": false, + "instant": false, + "legendFormat": "Inviter - {{namespace}}", + "range": true, + "refId": "F" + } + ], + "title": "[connection] time taken in background job Invitee/Inviter flow (guage)", + "type": "timeseries" + } + ], + "title": "Background Jobs - Connection", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 424, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 267, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_did_com_exchange_job_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[issuance] background job processing (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 389, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_issuer_send_offer_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "Offer pending - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_holder_req_pending_to_generated_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Request pending - {{namespace}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_holder_req_generated_to_sent_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Request generated - {{namespace}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_issuer_cred_received_to_pending_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Request received - {{namespace}}", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_issuer_cred_pending_to_generated_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Credential pending - {{namespace}}", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(issuance_flow_issuer_send_cred_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Credential generated - {{namespace}}", + "range": true, + "refId": "F" + } + ], + "title": "[issuance] background job total records processed (rate)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 30 + }, + "id": 419, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_issuer_send_offer_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "Offer pending - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_holder_req_pending_to_generated_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "hide": false, + "instant": false, + "legendFormat": "Request pending - {{namespace}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_holder_req_generated_to_sent_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "hide": false, + "instant": false, + "legendFormat": "Request generated - {{namespace}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_issuer_cred_received_to_pending_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "hide": false, + "instant": false, + "legendFormat": "Request received - {{namespace}}", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (issuance_flow_issuer_cred_pending_to_generated_flow_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "hide": false, + "instant": false, + "legendFormat": "Credential pending - {{namespace}}", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "rate(issuance_flow_issuer_send_cred_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "Credential generated - {{namespace}}", + "range": true, + "refId": "F" + } + ], + "title": "[issuance] background job total records processed (count)", + "type": "timeseries" + } + ], + "title": "Background Jobs - Issuance", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 426, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 268, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (present_proof_flow_did_com_exchange_job_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[present proof] pbackground job processing (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 422, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(present_proof_flow_prover_presentation_pending_to_generated_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "Presentation pending - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(present_proof_flow_prover_presentation_generated_to_sent_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Presentation generated - {{namespace}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Presentation recieved - {{namespace}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(present_proof_flow_verifier_request_pending_to_sent_all_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Request Pending (verifier) - {{namespace}}", + "range": true, + "refId": "E" + } + ], + "title": "[present proof] background job total records processed (rate)", + "type": "timeseries" + } + ], + "title": "Background Jobs - Presentation Proof", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 486, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": false, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "A" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "left" + }, + { + "id": "color", + "value": { + "fixedColor": "super-light-red", + "mode": "fixed" + } + }, + { + "id": "custom.axisLabel", + "value": "VUs" + }, + { + "id": "custom.scaleDistribution", + "value": { + "log": 10, + "type": "log" + } + }, + { + "id": "custom.showPoints", + "value": "never" + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "B" + }, + "properties": [ + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + }, + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + }, + { + "id": "custom.axisLabel", + "value": "RPS" + }, + { + "id": "custom.lineWidth", + "value": 3 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "D" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + }, + { + "id": "custom.lineWidth", + "value": 5 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "C" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + }, + { + "id": "unit", + "value": "s" + }, + { + "id": "custom.fillOpacity", + "value": 18 + }, + { + "id": "custom.lineInterpolation", + "value": "smooth" + }, + { + "id": "custom.axisLabel", + "value": "Response Time" + }, + { + "id": "decimals", + "value": 2 + } + ] + }, + { + "matcher": { + "id": "byFrameRefID", + "options": "E" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + }, + { + "id": "custom.axisPlacement", + "value": "left" + }, + { + "id": "unit", + "value": "percent" + } + ] + } + ] + }, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 513, + "interval": "5", + "options": { + "legend": { + "calcs": [ + "min", + "mean", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "timezones": [ + "browser" + ], + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "repeat": "scenario_label", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "k6_vus", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "VUs", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\"}[$__rate_interval]))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "Request Rate", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\", expected_response=\"false\"}[$__rate_interval]))", + "hide": true, + "instant": false, + "legendFormat": "Failed Requests Rate ", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(histogram_sum(rate(k6_group_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))\n/\nhistogram_count(rate(k6_group_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))) ", + "hide": false, + "legendFormat": "{{group}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\", expected_response=\"false\"}[$__rate_interval])) / sum(rate(k6_http_reqs_total{scenario_label=\"$scenario_label\"}[$__rate_interval])) * 100", + "hide": false, + "instant": false, + "legendFormat": "Failed Requests Percentage Rate", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "(histogram_sum(rate(k6_iteration_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))\n/\nhistogram_count(rate(k6_iteration_duration_seconds{scenario_label=\"$scenario_label\"}[$__rate_interval]))) ", + "hide": false, + "instant": false, + "legendFormat": "Iteration Duration", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": " sum(rate(k6_iterations_total{scenario_label=\"$scenario_label\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Iteration Rate", + "range": true, + "refId": "G" + } + ], + "title": "[k6] scenario group duration ($scenario_label)", + "type": "timeseries" + } + ], + "title": "K6 Scenario Results - All", + "type": "row" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 16, + "panels": [], + "title": "HTTP Metrics (Totals)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (status, namespace) (tapir_request_total{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "[http] - total requests (count)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (status, namespace) (irate(tapir_request_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "[http] - total requests (rate)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 33 + }, + "id": 18, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum(rate(tapir_request_duration_seconds_bucket{container=\"prism-agent-server\", cluster=\"$cluster\"}[5m])) by (le)\n", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "[http] total request duration (histogram)", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 33 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "(sum by (namespace) (irate(tapir_request_duration_seconds_sum{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))) / (sum by (namespace) (irate(tapir_request_duration_seconds_count{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "[http] average request duration over $__rate_interval", + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 22, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 3, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "B" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "unit", + "value": "none" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.scaleDistribution", + "value": { + "type": "linear" + } + } + ] + } + ] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 23, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "mean", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "repeat": "path", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "((sum by (namespace) (irate(tapir_request_duration_seconds_sum{container=\"prism-agent-server\", cluster=\"$cluster\", path=\"$path\"}[$__rate_interval]))) / (sum by (namespace) (irate(tapir_request_duration_seconds_count{container=\"prism-agent-server\", cluster=\"$cluster\", path=\"$path\"}[$__rate_interval]))))", + "hide": false, + "instant": false, + "legendFormat": "Average Duration - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (irate(tapir_request_total{container=\"prism-agent-server\", cluster=\"$cluster\", path=\"$path\"}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "Rate - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "Average request duration over ($__rate_interval) minute - [$path]", + "type": "timeseries" + } + ], + "title": "HTTP Metrics (per path - select at top of dashboard)", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 250, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 128 + }, + "id": 218, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (tapir_request_total{cluster=\"$cluster\", method=\"POST\", path=\"/connections\", container=\"prism-agent-server\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] number of connection create requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 128 + }, + "id": 234, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (tapir_request_total{cluster=\"$cluster\", method=\"POST\", path=\"/did-registrar/dids\", container=\"prism-agent-server\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[did] number of DID create requests", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 136 + }, + "id": 270, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (tapir_request_total{cluster=\"$cluster\", method=\"POST\", path=\"/present-proof/presentations\", container=\"prism-agent-server\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[proof] number of presentation create requests", + "type": "stat" + } + ], + "title": "Object Creation Metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 44 + }, + "id": 11, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "min", + "lastNotNull", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(jvm_memory_bytes_used{area=~\"$memarea\",container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "hide": false, + "interval": "", + "legendFormat": "Used - {{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(jvm_memory_bytes_max{area=~\"$memarea\",container=\"prism-agent-server\", cluster=\"$cluster\"}) by (namespace)", + "hide": false, + "interval": "", + "legendFormat": "Max - {{namespace}}", + "range": true, + "refId": "B" + } + ], + "title": "JVM Memory Used [$memarea]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 13, + "links": [], + "options": { + "legend": { + "calcs": [ + "min", + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(jvm_gc_collection_seconds_sum{container=\"prism-agent-server\", cluster=\"${cluster}\"}[$__rate_interval])) by (gc, namespace)", + "format": "time_series", + "interval": "60s", + "intervalFactor": 1, + "legendFormat": "{{gc}} - {{namespace}}", + "range": true, + "refId": "A" + } + ], + "title": "GC Time rate", + "type": "timeseries" + } + ], + "title": "JVM Metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 45 + }, + "id": 8, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "quota - requests" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#F2495C", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "quota - limits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FF9830", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 76 + }, + "id": 4, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{cluster=\"$cluster\", container=\"prism-agent-server\"}) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "quota - requests" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#F2495C", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "quota - limits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FF9830", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "A", + "mode": "none" + } + }, + { + "id": "custom.lineStyle", + "value": { + "dash": [ + 10, + 10 + ], + "fill": "dash" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 76 + }, + "id": 5, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(container_memory_working_set_bytes{job!=\"\", cluster=\"$cluster\", container=\"prism-agent-server\", image!=\"\"}) by (namespace)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Usage (w/o cache)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 84 + }, + "id": 6, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(container_network_receive_bytes_total{cluster=\"$cluster\", pod=~\"prism-agent-server.*\"}[$__rate_interval])) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Receive Bandwidth", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 84 + }, + "id": 7, + "interval": "1m", + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(container_network_transmit_bytes_total{cluster=\"$cluster\", pod=~\"prism-agent-server.*\"}[$__rate_interval])) by (namespace)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "Transmit Bandwidth", + "type": "timeseries" + } + ], + "title": "Container Metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 151, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "counter that tracks amount of successful attempts for an invitee to send a connection request to the inviter", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 47 + }, + "id": 74, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_invitee_connection_request_msg_success_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] - successful invitee -> inviter request [rate]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "counter that tracks amount of failed attempts for an invitee to send a connection request to the inviter", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 47 + }, + "id": 49, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "rate(connection_flow_invitee_connection_request_msg_failed_counter{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] failed invitee -> inviter request [rate]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "counter that tracks amount of successful attempts for an inviter to send a connection request to the invitee", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 55 + }, + "id": 126, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_inviter_connection_response_msg_success_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] successful inviter -> invitee response [rate]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "counter that tracks amount of failed attempts for an inviter to send a connection response to the invitee", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 55 + }, + "id": 127, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_inviter_connection_response_msg_failed_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] failed inviter -> invitee response [rate]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "counter that tracks the number of successful attempts to process connection record by the inviter, this includes getting a record from db, sending a message, and updating the status of the record", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 63 + }, + "id": 73, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_inviter_process_connection_record_success_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] successful process connection record (rate)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": " counter that tracks the number of failed attempts to process connection record by the inviter, this includes getting a record from db, sending a message, and updating the status of the record", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 63 + }, + "id": 75, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (rate(connection_flow_inviter_process_connection_record_failed_counter_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] failed process connection record (rate)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "the time it takes (in ms) for the record to be moved from the pending state to the response sent state on the inviter side (Gauge). Connection Id is provided for each metric", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 71 + }, + "id": 76, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_inviter_pending_to_res_sent_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] pending state to response sent state (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "the time it takes (in ms) for an inviter to send a connection response to the invitee (Gauge). Connection Id is provided for each metric", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 71 + }, + "id": 125, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_inviter_send_connection_response_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] inviter send connection response (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "the time it takes (in ms) for an inviter to process a record (Gauge). Connection Id is provided for each metric", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 79 + }, + "id": 100, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_did_com_exchange_job_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] inviter process record (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "the time it takes (in ms) for an inviter to process a record (Gauge). Connection Id is provided for each metric", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 79 + }, + "id": 175, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_did_com_exchange_job_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] inviter process record (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "All invitee gauge added together - time taken to send a connection request + time taking to move it from pending to sent + time taken to process a record", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 87 + }, + "id": 199, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_invitee_send_connection_request_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"} + connection_flow_invitee_pending_to_req_sent_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"} + connection_flow_invitee_process_connection_record_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] invitee e2e processing (time)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "All inviter gauge added together - time taken to send a connection request + time taking to move it from pending to sent + time taken to process a record", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 87 + }, + "id": 200, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.0-60477", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace) (connection_flow_inviter_send_connection_response_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"} + connection_flow_inviter_pending_to_res_sent_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"} + connection_flow_inviter_process_connection_record_ms_gauge{container=\"prism-agent-server\", cluster=\"$cluster\"})", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[connection] inviter e2e processing (time)", + "type": "timeseries" + } + ], + "title": "Detailed - Connection Metrics (incubating)", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 14, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "min", + "lastNotNull", + "max", + "mean" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(process_resident_memory_bytes{container=\"prism-agent-server\", cluster=\"$cluster\"}) by(namespace)", + "hide": false, + "interval": "", + "legendFormat": "{{namespace}} Resident", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(process_virtual_memory_bytes{container=\"prism-agent-server\", cluster=\"$cluster\"}) by(namespace)", + "hide": false, + "interval": "", + "legendFormat": "{{namespace}} Virtual", + "range": true, + "refId": "B" + } + ], + "title": "Process Memory", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "dark-red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 12, + "y": 32 + }, + "id": 202, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "(sum(irate(tapir_request_total{container=\"prism-agent-server\", cluster=\"$cluster\", status=~\"5..\"}[$__rate_interval]))) / (sum(irate(tapir_request_total{container=\"prism-agent-server\", cluster=\"$cluster\"}[$__rate_interval])))\n", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "[http] errors versus total requests (ratio)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 286, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "(histogram_sum(rate(k6_group_duration_seconds{scenario_label=\"connection-flow-smoke\"}[$__rate_interval]))\n/\nhistogram_count(rate(k6_group_duration_seconds{scenario_label=\"connection-flow-smoke\"}[$__rate_interval]))) ", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{group}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "[k6] [connection] average response duration (time)", + "type": "timeseries" + } + ], + "title": "Incubating", + "type": "row" + } + ], + "refresh": "5s", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "local-cluster", + "value": "local-cluster" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(cluster)", + "hide": 0, + "includeAll": false, + "label": "Cluster", + "multi": false, + "name": "cluster", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(cluster)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(jvm_memory_bytes_used{cluster=\"$cluster\", container=\"prism-agent-server\"},area)", + "hide": 2, + "includeAll": true, + "multi": true, + "name": "memarea", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(jvm_memory_bytes_used{cluster=\"$cluster\", container=\"prism-agent-server\"},area)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": "", + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(tapir_request_total,path)", + "description": "API Path for Tapir", + "hide": 0, + "includeAll": true, + "label": "Path", + "multi": true, + "name": "path", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(tapir_request_total,path)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "grafanacloud-prom", + "value": "PA396B7035A9690C9" + }, + "hide": 0, + "includeAll": false, + "label": "Prometheus Datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "grafanacloud-logs", + "value": "P542D2CD8B7CD28BD" + }, + "hide": 0, + "includeAll": false, + "label": "Loki Datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "PA396B7035A9690C9" + }, + "definition": "label_values(scenario_label)", + "hide": 0, + "includeAll": true, + "label": "Scenario Label", + "multi": false, + "name": "scenario_label", + "options": [], + "query": { + "query": "label_values(scenario_label)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Open Enterprise Agent - Kubernetes Overview", + "uid": "oea-k8s-overview", + "version": 24, + "weekStart": "" +} \ No newline at end of file diff --git a/infrastructure/single-tenant-testing-stack/docker-compose.yml b/infrastructure/single-tenant-testing-stack/docker-compose.yml index 898453a037..b853bdf186 100644 --- a/infrastructure/single-tenant-testing-stack/docker-compose.yml +++ b/infrastructure/single-tenant-testing-stack/docker-compose.yml @@ -15,7 +15,7 @@ services: ports: - 5432:5432 healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "agent"] interval: 10s timeout: 5s retries: 5 @@ -33,7 +33,7 @@ services: ports: - 5433:5432 healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "agent"] interval: 10s timeout: 5s retries: 5 @@ -51,7 +51,7 @@ services: ports: - 5434:5432 healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "agent"] interval: 10s timeout: 5s retries: 5 @@ -121,14 +121,14 @@ services: API_KEY_AUTHENTICATE_AS_DEFAULT_USER: API_KEY_AUTO_PROVISIONING: ISSUE_BG_JOB_RECORDS_LIMIT: 25 - ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds - ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + ISSUE_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 20 PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 - PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds - PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 20 CONNECT_BG_JOB_RECORDS_LIMIT: 25 - CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds - CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 20 depends_on: issuer-db: condition: service_healthy @@ -141,6 +141,17 @@ services: retries: 5 extra_hosts: - "host.docker.internal:host-gateway" + command: [ + -Dcom.sun.management.jmxremote, + -Dcom.sun.management.jmxremote.port=9095, + -Dcom.sun.management.jmxremote.rmi.port=9095, + -Dcom.sun.management.jmxremote.ssl=false, + -Dcom.sun.management.jmxremote.local.only=true, + -Dcom.sun.management.jmxremote.authenticate=false, + -Djava.rmi.server.hostname=127.0.0.1 + ] + ports: + - 9095:9095 verifier-oea: image: ghcr.io/input-output-hk/prism-agent:${PRISM_AGENT_VERSION} @@ -179,14 +190,14 @@ services: API_KEY_AUTHENTICATE_AS_DEFAULT_USER: API_KEY_AUTO_PROVISIONING: ISSUE_BG_JOB_RECORDS_LIMIT: 25 - ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds - ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + ISSUE_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 20 PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 - PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds - PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 20 CONNECT_BG_JOB_RECORDS_LIMIT: 25 - CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds - CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 20 depends_on: verifier-db: condition: service_healthy @@ -200,6 +211,20 @@ services: extra_hosts: - "host.docker.internal:host-gateway" + issuer-oea-postgres-exporter: + image: quay.io/prometheuscommunity/postgres-exporter + ports: + - "9995:9187" + environment: + - DATA_SOURCE_NAME=postgresql://postgres:postgres@holder-db:5432/postgres?sslmode=disable + + holder-oea-postgres-exporter: + image: quay.io/prometheuscommunity/postgres-exporter + ports: + - "9996:9187" + environment: + - DATA_SOURCE_NAME=postgresql://postgres:postgres@issuer-db:5432/postgres?sslmode=disable + holder-oea: image: ghcr.io/input-output-hk/prism-agent:${PRISM_AGENT_VERSION} environment: @@ -232,14 +257,14 @@ services: # GLOBAL_WEBHOOK_API_KEY: # WEBHOOK_PARALLELISM: ISSUE_BG_JOB_RECORDS_LIMIT: 25 - ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds - ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + ISSUE_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 20 PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 - PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds - PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 20 CONNECT_BG_JOB_RECORDS_LIMIT: 25 - CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds - CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECURRENCE_DELAY: 100 milliseconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 20 ADMIN_TOKEN: API_KEY_SALT: API_KEY_ENABLED: @@ -257,6 +282,17 @@ services: retries: 5 extra_hosts: - "host.docker.internal:host-gateway" + command: [ + -Dcom.sun.management.jmxremote, + -Dcom.sun.management.jmxremote.port=9096, + -Dcom.sun.management.jmxremote.rmi.port=9096, + -Dcom.sun.management.jmxremote.ssl=false, + -Dcom.sun.management.jmxremote.local.only=true, + -Dcom.sun.management.jmxremote.authenticate=false, + -Djava.rmi.server.hostname=127.0.0.1 + ] + ports: + - 9096:9096 apisix: image: apache/apisix:2.15.0-alpine diff --git a/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh b/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh index 57166c29e9..16fd36ac0d 100755 --- a/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh +++ b/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh @@ -33,11 +33,11 @@ echo "--------------------------------------" cd ${SCRIPT_DIR}/../../tests/performance-tests/atala-performance-tests-k6 yarn install yarn webpack - k6 run -e SCENARIO_LABEL=create-prism-did-smoke dist/create-prism-did-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=credential-offer-smoke dist/credential-offer-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=credential-schema-smoke dist/credential-schema-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=did-publishing-smoke dist/did-publishing-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=connection-flow-smoke dist/connection-flow-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=issuance-flow-smoke dist/issuance-flow-test.js -o experimental-prometheus-rw - k6 run -e SCENARIO_LABEL=present-proof-flow-smoke dist/present-proof-flow-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-create-prism-did-smoke dist/create-prism-did-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-credential-offer-smoke dist/credential-offer-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-credential-schema-smoke dist/credential-schema-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-did-publishing-smoke dist/did-publishing-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-connection-flow-smoke dist/connection-flow-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-issuance-flow-smoke dist/issuance-flow-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=st-present-proof-flow-smoke dist/present-proof-flow-test.js -o experimental-prometheus-rw ) From a164361040f405491557c28bf82f9b82b3b24be7 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 18 Jan 2024 15:10:04 +0000 Subject: [PATCH 03/26] feat(prism-agent): add exception handling to k6 perf tests Signed-off-by: David Poltorak --- .../atala-performance-tests-k6/README.md | 16 +++++++ .../src/common/Config.ts | 4 +- .../src/common/ConnectionService.ts | 39 +++++++++-------- .../src/common/CredentialsService.ts | 43 ++++++++++++++----- .../src/common/DidService.ts | 40 ++++++++++++++--- .../src/common/HttpService.ts | 7 ++- .../src/common/ProofsService.ts | 36 ++++++++++++---- .../src/k6chaijs.js | 4 ++ .../src/tests/common.ts | 16 +++---- .../credentials/credential-offer-test.ts | 15 +++---- .../credentials/credential-schema-test.ts | 8 ++-- .../src/tests/dids/create-prism-did-test.ts | 6 +-- .../src/tests/dids/did-publishing-test.ts | 6 +-- .../src/tests/flows/connection-flow-test.ts | 9 ++-- .../src/tests/flows/issuance-flow-test.ts | 35 +++++++-------- .../tests/flows/present-proof-flow-test.ts | 30 ++++++------- 16 files changed, 204 insertions(+), 110 deletions(-) create mode 100644 tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js diff --git a/tests/performance-tests/atala-performance-tests-k6/README.md b/tests/performance-tests/atala-performance-tests-k6/README.md index 79df0d645b..614f010413 100644 --- a/tests/performance-tests/atala-performance-tests-k6/README.md +++ b/tests/performance-tests/atala-performance-tests-k6/README.md @@ -29,3 +29,19 @@ Once that is done, we can run our script the same way we usually do, for instanc ```bash $ k6 run dist/connection-flow-test.js ``` + +## Debugging Tests + +k6 can be configured to log the HTTP request and responses that it makes during test execution. This is useful to debug errors that happen in tests when logs or k6 output does not contain the reason for a failure. + +For example, if many requests result in 503s due to HTTP timeouts, there aren't many logs available to show when and why this happened. + +To enable k6 to output requests and responses - add the `--http-debug` flag to the k6 test execution command + +For example: `k6 run -e SCENARIO_LABEL=create-prism-did-smoke dist/create-prism-did-test.js -o experimental-prometheus-rw --http-debug` + +By default, k6 does not output the body of the request/response - only the headers. + +Add the flag `--http-debug="full"` to include the body of both request/response. + +For example: `k6 run -e SCENARIO_LABEL=create-prism-did-smoke dist/create-prism-did-test.js -o experimental-prometheus-rw --http-debug=full` diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts index 5adfab1e37..71ae4c27f9 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts @@ -1,8 +1,8 @@ /** * Maximum number of iterations for the waiting loop. - * If not provided, the default value is 40. + * If not provided, the default value is 100. */ -export const WAITING_LOOP_MAX_ITERATIONS = Number(__ENV.MY_USER_AGENT) || 500; +export const WAITING_LOOP_MAX_ITERATIONS = Number(__ENV.MY_USER_AGENT) || 100; /** * Pause interval in seconds for each iteration of the waiting loop. diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts index d43acd6678..bec09da9f8 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts @@ -2,24 +2,15 @@ import { Connection, ConnectionInvitation, ConnectionStateEnum } from "@input-output-hk/prism-typescript-client"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; -import { HttpService } from "./HttpService"; -import { sleep } from "k6"; +import { HttpService, statusChangeTimeouts } from "./HttpService"; +import { sleep, fail } from "k6"; +import {RefinedResponse, ResponseType} from "k6/http"; /** * A service class for managing connections in the application. * Extends the HttpService class. */ export class ConnectionService extends HttpService { - - /** - * Retrieves all connections. - * @returns {Connection[]} An array of connections. - */ - getConnections(): Connection[] { - const res = this.get("connections"); - const connections = res.json("contents") as unknown as Connection[]; - return connections; - } /** * Retrieves a specific connection by ID. @@ -28,8 +19,11 @@ export class ConnectionService extends HttpService { */ getConnection(connectionId: string): Connection { const res = this.get(`connections/${connectionId}`); - const connection = this.toJson(res) as unknown as Connection; - return connection; + try { + return this.toJson(res) as unknown as Connection; + } catch { + fail("Failed to parse JSON as connection") + } } /** @@ -38,8 +32,12 @@ export class ConnectionService extends HttpService { */ createConnection(): Connection { const payload = { label: "test" }; - const connection = this.toJson(this.post("connections", payload)) as unknown as Connection; - return connection; + const res = this.post("connections", payload) + try { + return this.toJson(res) as unknown as Connection; + } catch { + fail("Failed to parse JSON as connection") + } } /** @@ -50,7 +48,11 @@ export class ConnectionService extends HttpService { acceptConnectionInvitation(invitation: ConnectionInvitation): Connection { const payload = { invitation: this.invitationFromUrl(invitation.invitationUrl) }; const res = this.post("connection-invitations", payload, 200); - return this.toJson(res) as unknown as Connection; + try { + return this.toJson(res) as unknown as Connection; + } catch { + fail("Failed to parse JSON as connection") + } } /** @@ -69,7 +71,8 @@ export class ConnectionService extends HttpService { iterations++; } while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS); if (state !== requiredState) { - throw new Error(`Connection state is ${state}, required ${requiredState}`); + statusChangeTimeouts.add(1) + fail("Connection state is ${state}, required ${requiredState}"); } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts index c55a37ca1d..0ce9290275 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts @@ -1,6 +1,6 @@ -import { sleep } from "k6"; -import { HttpService } from "./HttpService"; -import { ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; +import {fail, sleep} from "k6"; +import { HttpService, statusChangeTimeouts } from "./HttpService"; +import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; import { crypto } from "k6/experimental/webcrypto"; @@ -26,13 +26,16 @@ export class CredentialsService extends HttpService { "drivingLicenseID": "Test", "drivingClass": 1 }, - "schemaId": "${ISSUER_AGENT_URL.replace("localhost", "host.docker.internal")}/schema-registry/schemas/${schema.guid}", "issuingDID": "${issuingDid}", "connectionId": "${connection.connectionId}", "automaticIssuance": false }`; const res = this.post("issue-credentials/credential-offers", payload); - return this.toJson(res) as unknown as IssueCredentialRecord; + try { + return this.toJson(res) as unknown as IssueCredentialRecord; + } catch { + fail("Failed to parse JSON as IssueCredentialRecord") + } } createCredentialSchema(issuingDid: string): CredentialSchemaResponse { @@ -86,7 +89,11 @@ export class CredentialsService extends HttpService { } ` const res = this.post("schema-registry/schemas", payload); - return this.toJson(res) as unknown as CredentialSchemaResponse; + try { + return this.toJson(res) as unknown as CredentialSchemaResponse; + } catch { + fail("Failed to parse JSON as CredentialSchemaResponse") + } } /** @@ -96,7 +103,11 @@ export class CredentialsService extends HttpService { */ getCredentialRecord(record: IssueCredentialRecord): IssueCredentialRecord { const res = this.get(`issue-credentials/records/${record.recordId}`); - return this.toJson(res) as unknown as IssueCredentialRecord; + try { + return this.toJson(res) as unknown as IssueCredentialRecord; + } catch { + fail("Failed to parse JSON as IssueCredentialRecord") + } } /** @@ -117,7 +128,11 @@ export class CredentialsService extends HttpService { acceptCredentialOffer(record: IssueCredentialRecord, subjectDid: string): IssueCredentialRecord { const payload = { subjectId: subjectDid }; const res = this.post(`issue-credentials/records/${record.recordId}/accept-offer`, payload, 200); - return this.toJson(res) as unknown as IssueCredentialRecord; + try { + return this.toJson(res) as unknown as IssueCredentialRecord; + } catch { + fail("Failed to parse JSON as IssueCredentialRecord") + } } /** @@ -127,7 +142,11 @@ export class CredentialsService extends HttpService { */ issueCredential(record: IssueCredentialRecord): IssueCredentialRecord { const res = this.post(`issue-credentials/records/${record.recordId}/issue-credential`, null, 200); - return this.toJson(res) as unknown as IssueCredentialRecord; + try { + return this.toJson(res) as unknown as IssueCredentialRecord; + } catch { + fail("Failed to parse JSON as IssueCredentialRecord") + } } /** @@ -149,7 +168,8 @@ export class CredentialsService extends HttpService { sleep(WAITING_LOOP_PAUSE_INTERVAL); iterations++; } while (iterations < WAITING_LOOP_MAX_ITERATIONS); - throw new Error(`Record with thid=${thid} not achieved during the waiting loop`); + statusChangeTimeouts.add(1) + fail(`Record not found in Offer Received for thid during the waiting loop`); } /** @@ -168,7 +188,8 @@ export class CredentialsService extends HttpService { iterations++; } while (currentState !== state && iterations < WAITING_LOOP_MAX_ITERATIONS); if (currentState !== state) { - throw new Error(`Credential is not ${state} after the waiting loop`); + statusChangeTimeouts.add(1) + fail(`Credential is not ${state} after the waiting loop`); } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts index 95f1cc60c8..8416147099 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts @@ -1,9 +1,15 @@ /*global __ENV*/ -import { HttpService } from "./HttpService"; +import { HttpService, statusChangeTimeouts } from "./HttpService"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; -import { CreateManagedDIDResponse, DIDDocument, DidOperationSubmission, ManagedDID } from "@input-output-hk/prism-typescript-client"; -import {sleep} from "k6"; +import { + CreateManagedDIDResponse, + DIDDocument, + DidOperationSubmission, + IssueCredentialRecord, + ManagedDID +} from "@input-output-hk/prism-typescript-client"; +import {fail, sleep} from "k6"; /** @@ -19,7 +25,11 @@ export class DidService extends HttpService { */ getDid(did: string): ManagedDID { const res = this.get(`did-registrar/dids/${did}`); - return this.toJson(res) as unknown as ManagedDID; + try { + return this.toJson(res) as unknown as ManagedDID; + } catch { + fail("Failed to parse JSON as ManagedDID") + } } /** @@ -29,7 +39,11 @@ export class DidService extends HttpService { */ resolveDid(did: string): DIDDocument { const res = this.get(`dids/${did}`); - return this.toJson(res) as unknown as DIDDocument; + try { + return this.toJson(res) as unknown as DIDDocument; + } catch { + fail("Failed to parse JSON as DIDDocument") + } } /** @@ -39,7 +53,11 @@ export class DidService extends HttpService { */ publishDid(did: string): DidOperationSubmission { const res = this.post(`did-registrar/dids/${did}/publications`, null, 202); - return this.toJson(res).scheduledOperation as unknown as DidOperationSubmission; + try { + return this.toJson(res).scheduledOperation as unknown as DidOperationSubmission; + } catch { + fail("Failed to parse JSON as DidOperationSubmission") + } } /** @@ -49,7 +67,11 @@ export class DidService extends HttpService { */ createUnpublishedDid(documentTemplate: string): CreateManagedDIDResponse { const res = this.post("did-registrar/dids", documentTemplate); - return this.toJson(res) as unknown as CreateManagedDIDResponse; + try { + return this.toJson(res) as unknown as CreateManagedDIDResponse; + } catch { + fail("Failed to parse JSON as CreateManagedDIDResponse") + } } /** @@ -67,6 +89,10 @@ export class DidService extends HttpService { sleep(WAITING_LOOP_PAUSE_INTERVAL); iterations++; } while (didState !== state && iterations < WAITING_LOOP_MAX_ITERATIONS); + if (didState !== state) { + statusChangeTimeouts.add(1) + fail(`DID is not ${state} after the waiting loop`); + } } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts index 4b98159cc1..87b19fac86 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts @@ -2,10 +2,12 @@ import http from 'k6/http'; import { check } from 'k6'; import { RefinedResponse, ResponseType, RequestBody } from 'k6/http'; +import { Counter } from 'k6/metrics'; +export let statusChangeTimeouts = new Counter('status_change_timeouts'); /** * HttpService provides convenience methods for making HTTP requests using the k6 library. - * + * * - reduces boilerplate code * - adds basic HTTP status code checks * - adds API key header @@ -117,4 +119,7 @@ export class HttpService { }); return res; } + + + } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts index f274b8f0de..e219d44e71 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts @@ -1,6 +1,6 @@ -import { HttpService } from "./HttpService"; -import { sleep } from "k6"; -import { Connection, PresentationStatus } from "@input-output-hk/prism-typescript-client"; +import { HttpService, statusChangeTimeouts } from "./HttpService"; +import {fail, sleep} from "k6"; +import {Connection, IssueCredentialRecord, PresentationStatus} from "@input-output-hk/prism-typescript-client"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import vu from "k6/execution"; @@ -34,7 +34,11 @@ export class ProofsService extends HttpService { ] }` const res = this.post("present-proof/presentations", payload); - return this.toJson(res).presentationId as string; + try { + return this.toJson(res).presentationId as string; + } catch { + fail("Failed to parse JSON as presentationId string") + } } /** @@ -51,7 +55,11 @@ export class ProofsService extends HttpService { ] }` const res = this.patch(`present-proof/presentations/${presentation.presentationId}`, payload); - return this.toJson(res).presentationId as string; + try { + return this.toJson(res).presentationId as string; + } catch { + fail("Failed to parse JSON as presentationId string") + } } /** @@ -61,7 +69,11 @@ export class ProofsService extends HttpService { */ getPresentation(presentationId: string): PresentationStatus { const res = this.get(`present-proof/presentations/${presentationId}`); - return this.toJson(res) as unknown as PresentationStatus; + try { + return this.toJson(res) as unknown as PresentationStatus; + } catch { + fail("Failed to parse JSON as PresentationStatus") + } } /** @@ -70,7 +82,11 @@ export class ProofsService extends HttpService { */ getPresentations(thid: string): PresentationStatus[] { const res = this.get(`present-proof/presentations?thid=${thid}`); - return this.toJson(res).contents as unknown as PresentationStatus[]; + try { + return this.toJson(res).contents as unknown as PresentationStatus[]; + } catch { + fail("Failed to parse JSON as PresentationStatus[]") + } } /** @@ -91,7 +107,8 @@ export class ProofsService extends HttpService { sleep(WAITING_LOOP_PAUSE_INTERVAL); iterations++; } while (iterations < WAITING_LOOP_MAX_ITERATIONS); - throw new Error(`Presentation with offerId=${vu.vu.idInTest} not achieved during the waiting loop`); + statusChangeTimeouts.add(1) + fail(`Presentation with offerId not achieved during the waiting loop`); } /** @@ -109,7 +126,8 @@ export class ProofsService extends HttpService { iterations++; } while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS); if (state !== requiredState) { - throw new Error(`Presentation state is ${state}, required ${requiredState}`); + statusChangeTimeouts.add(1) + fail(`Presentation state is ${state}, required ${requiredState}`); } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js b/tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js new file mode 100644 index 0000000000..c7d70377f1 --- /dev/null +++ b/tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js @@ -0,0 +1,4 @@ +"use strict";var Er=Object.create;var Re=Object.defineProperty;var Ar=Object.getOwnPropertyDescriptor;var Pr=Object.getOwnPropertyNames;var Or=Object.getPrototypeOf,Nr=Object.prototype.hasOwnProperty;var O=(s,o)=>()=>(o||s((o={exports:{}}).exports,o),o.exports),jr=(s,o)=>{for(var t in o)Re(s,t,{get:o[t],enumerable:!0})},bt=(s,o,t,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of Pr(o))!Nr.call(s,e)&&e!==t&&Re(s,e,{get:()=>o[e],enumerable:!(f=Ar(o,e))||f.enumerable});return s};var qr=(s,o,t)=>(t=s!=null?Er(Or(s)):{},bt(o||!s||!s.__esModule?Re(t,"default",{value:s,enumerable:!0}):t,s)),Tr=s=>bt(Re({},"__esModule",{value:!0}),s);var Qe=O((Xo,vt)=>{function mt(){var s=[].slice.call(arguments);function o(t,f){Object.keys(f).forEach(function(e){~s.indexOf(e)||(t[e]=f[e])})}return function(){for(var f=[].slice.call(arguments),e=0,n={};e{"use strict";function xt(s,o){return typeof s>"u"||s===null?!1:o in Object(s)}function St(s){var o=s.replace(/([^\\])\[/g,"$1.["),t=o.match(/(\\\.|[^.]+?)+/g);return t.map(function(e){if(e==="constructor"||e==="__proto__"||e==="prototype")return{};var n=/^\[(\d+)\]$/,r=n.exec(e),i=null;return r?i={i:parseFloat(r[1])}:i={p:e.replace(/\\([.[\]])/g,"$1")},i})}function wt(s,o,t){var f=s,e=null;t=typeof t>"u"?o.length:t;for(var n=0;n"u"?f=f[r.i]:f=f[r.p],n===t-1&&(e=f))}return e}function Dr(s,o,t){for(var f=s,e=t.length,n=null,r=0;r"u"?n.i:n.p,f[i]=o;else if(typeof n.p<"u"&&f[n.p])f=f[n.p];else if(typeof n.i<"u"&&f[n.i])f=f[n.i];else{var v=t[r+1];i=typeof n.p>"u"?n.i:n.p,l=typeof v.p>"u"?[]:{},f[i]=l,f=f[i]}}}function Mt(s,o){var t=St(o),f=t[t.length-1],e={parent:t.length>1?wt(s,t,t.length-1):s,name:f.p||f.i,value:wt(s,t)};return e.exists=xt(e.parent,e.name),e}function Ir(s,o){var t=Mt(s,o);return t.value}function kr(s,o,t){var f=St(o);return Dr(s,t,f),s}Et.exports={hasProperty:xt,getPathInfo:Mt,getPathValue:Ir,setPathValue:kr}});var Y=O((ei,Pt)=>{Pt.exports=function(o,t,f){var e=o.__flags||(o.__flags=Object.create(null));if(arguments.length===3)e[t]=f;else return e[t]}});var Nt=O((ti,Ot)=>{var Cr=Y();Ot.exports=function(o,t){var f=Cr(o,"negate"),e=t[0];return f?!e:e}});var Me=O((Ye,Xe)=>{(function(s,o){typeof Ye=="object"&&typeof Xe<"u"?Xe.exports=o():typeof define=="function"&&define.amd?define(o):s.typeDetect=o()})(Ye,function(){"use strict";var s=typeof Promise=="function",o=typeof self=="object"?self:global,t=typeof Symbol<"u",f=typeof Map<"u",e=typeof Set<"u",n=typeof WeakMap<"u",r=typeof WeakSet<"u",i=typeof DataView<"u",l=t&&typeof Symbol.iterator<"u",v=t&&typeof Symbol.toStringTag<"u",M=e&&typeof Set.prototype.entries=="function",K=f&&typeof Map.prototype.entries=="function",H=M&&Object.getPrototypeOf(new Set().entries()),V=K&&Object.getPrototypeOf(new Map().entries()),B=l&&typeof Array.prototype[Symbol.iterator]=="function",ee=B&&Object.getPrototypeOf([][Symbol.iterator]()),Z=l&&typeof String.prototype[Symbol.iterator]=="function",le=Z&&Object.getPrototypeOf(""[Symbol.iterator]()),he=8,de=-1;function pe(F){var se=typeof F;if(se!=="object")return se;if(F===null)return"null";if(F===o)return"global";if(Array.isArray(F)&&(v===!1||!(Symbol.toStringTag in F)))return"Array";if(typeof window=="object"&&window!==null){if(typeof window.location=="object"&&F===window.location)return"Location";if(typeof window.document=="object"&&F===window.document)return"Document";if(typeof window.navigator=="object"){if(typeof window.navigator.mimeTypes=="object"&&F===window.navigator.mimeTypes)return"MimeTypeArray";if(typeof window.navigator.plugins=="object"&&F===window.navigator.plugins)return"PluginArray"}if((typeof window.HTMLElement=="function"||typeof window.HTMLElement=="object")&&F instanceof window.HTMLElement){if(F.tagName==="BLOCKQUOTE")return"HTMLQuoteElement";if(F.tagName==="TD")return"HTMLTableDataCellElement";if(F.tagName==="TH")return"HTMLTableHeaderCellElement"}}var W=v&&F[Symbol.toStringTag];if(typeof W=="string")return W;var R=Object.getPrototypeOf(F);return R===RegExp.prototype?"RegExp":R===Date.prototype?"Date":s&&R===Promise.prototype?"Promise":e&&R===Set.prototype?"Set":f&&R===Map.prototype?"Map":r&&R===WeakSet.prototype?"WeakSet":n&&R===WeakMap.prototype?"WeakMap":i&&R===DataView.prototype?"DataView":f&&R===V?"Map Iterator":e&&R===H?"Set Iterator":B&&R===ee?"Array Iterator":Z&&R===le?"String Iterator":R===null?"Object":Object.prototype.toString.call(F).slice(he,de)}return pe})});var qt=O((ni,jt)=>{var zr=Qe(),He=Y(),Br=Me();jt.exports=function(o,t){var f=He(o,"message"),e=He(o,"ssfi");f=f?f+": ":"",o=He(o,"object"),t=t.map(function(i){return i.toLowerCase()}),t.sort();var n=t.map(function(i,l){var v=~["a","e","i","o","u"].indexOf(i.charAt(0))?"an":"a",M=t.length>1&&l===t.length-1?"or ":"";return M+v+" "+i}).join(", "),r=Br(o).toLowerCase();if(!t.some(function(i){return r===i}))throw new zr(f+"object tested must be "+n+", but "+r+" given",void 0,e)}});var et=O((ri,Tt)=>{Tt.exports=function(o,t){return t.length>4?t[4]:o._obj}});var tt=O((oi,Dt)=>{"use strict";var Fr=Function.prototype.toString,Vr=/\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/;function Rr(s){if(typeof s!="function")return null;var o="";if(typeof Function.prototype.name>"u"&&typeof s.name>"u"){var t=Fr.call(s).match(Vr);t&&(o=t[1])}else o=s.name;return o}Dt.exports=Rr});var It=O(()=>{});var Ct=O((Le,kt)=>{(function(s,o){typeof Le=="object"&&typeof kt<"u"?o(Le):typeof define=="function"&&define.amd?define(["exports"],o):(s=typeof globalThis<"u"?globalThis:s||self,o(s.loupe={}))})(Le,function(s){"use strict";function o(u){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?o=function(c){return typeof c}:o=function(c){return c&&typeof Symbol=="function"&&c.constructor===Symbol&&c!==Symbol.prototype?"symbol":typeof c},o(u)}function t(u,c){return f(u)||e(u,c)||n(u,c)||i()}function f(u){if(Array.isArray(u))return u}function e(u,c){if(!(typeof Symbol>"u"||!(Symbol.iterator in Object(u)))){var g=[],x=!0,E=!1,N=void 0;try{for(var D=u[Symbol.iterator](),z;!(x=(z=D.next()).done)&&(g.push(z.value),!(c&&g.length===c));x=!0);}catch(_){E=!0,N=_}finally{try{!x&&D.return!=null&&D.return()}finally{if(E)throw N}}return g}}function n(u,c){if(!!u){if(typeof u=="string")return r(u,c);var g=Object.prototype.toString.call(u).slice(8,-1);if(g==="Object"&&u.constructor&&(g=u.constructor.name),g==="Map"||g==="Set")return Array.from(u);if(g==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(g))return r(u,c)}}function r(u,c){(c==null||c>u.length)&&(c=u.length);for(var g=0,x=new Array(c);g0&&arguments[0]!==void 0?arguments[0]:{},c=u.showHidden,g=c===void 0?!1:c,x=u.depth,E=x===void 0?2:x,N=u.colors,D=N===void 0?!1:N,z=u.customInspect,_=z===void 0?!0:z,U=u.showProxy,Q=U===void 0?!1:U,ae=u.maxArrayLength,Je=ae===void 0?1/0:ae,xe=u.breakLength,be=xe===void 0?1/0:xe,Se=u.seen,xr=Se===void 0?[]:Se,gt=u.truncate,Sr=gt===void 0?1/0:gt,yt=u.stylize,Mr=yt===void 0?String:yt,Ze={showHidden:Boolean(g),depth:Number(E),colors:Boolean(D),customInspect:Boolean(_),showProxy:Boolean(Q),maxArrayLength:Number(Je),breakLength:Number(be),truncate:Number(Sr),seen:xr,stylize:Mr};return Ze.colors&&(Ze.stylize=K),Ze}function V(u,c){var g=arguments.length>2&&arguments[2]!==void 0?arguments[2]:M;u=String(u);var x=g.length,E=u.length;return x>c&&E>x?g:E>c&&E>x?"".concat(u.slice(0,c-x)).concat(g):u}function B(u,c,g){var x=arguments.length>3&&arguments[3]!==void 0?arguments[3]:", ";g=g||c.inspect;var E=u.length;if(E===0)return"";for(var N=c.truncate,D="",z="",_="",U=0;UN&&D.length+_.length<=N||!Q&&!ae&&Se>N||(z=Q?"":g(u[U+1],c)+(ae?"":x),!Q&&ae&&Se>N&&be+z.length>N))break;if(D+=xe,!Q&&!ae&&be+z.length>=N){_="".concat(M,"(").concat(u.length-U-1,")");break}_=""}return"".concat(D).concat(_)}function ee(u){return u.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/)?u:JSON.stringify(u).replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'")}function Z(u,c){var g=t(u,2),x=g[0],E=g[1];return c.truncate-=2,typeof x=="string"?x=ee(x):typeof x!="number"&&(x="[".concat(c.inspect(x,c),"]")),c.truncate-=x.length,E=c.inspect(E,c),"".concat(x,": ").concat(E)}function le(u,c){var g=Object.keys(u).slice(u.length);if(!u.length&&!g.length)return"[]";c.truncate-=4;var x=B(u,c);c.truncate-=x.length;var E="";return g.length&&(E=B(g.map(function(N){return[N,u[N]]}),c,Z)),"[ ".concat(x).concat(E?", ".concat(E):""," ]")}var he=Function.prototype.toString,de=/\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/;function pe(u){if(typeof u!="function")return null;var c="";if(typeof Function.prototype.name>"u"&&typeof u.name>"u"){var g=he.call(u).match(de);g&&(c=g[1])}else c=u.name;return c}var F=pe,se=function(c){return typeof Buffer=="function"&&c instanceof Buffer?"Buffer":c[Symbol.toStringTag]?c[Symbol.toStringTag]:F(c.constructor)};function W(u,c){var g=se(u);c.truncate-=g.length+4;var x=Object.keys(u).slice(u.length);if(!u.length&&!x.length)return"".concat(g,"[]");for(var E="",N=0;N ").concat(E)}function Te(u){var c=[];return u.forEach(function(g,x){c.push([x,g])}),c}function De(u,c){var g=u.size-1;return g<=0?"Map{}":(c.truncate-=7,"Map{ ".concat(B(Te(u),c,qe)," }"))}var We=Number.isNaN||function(u){return u!==u};function Ie(u,c){return We(u)?c.stylize("NaN","number"):u===1/0?c.stylize("Infinity","number"):u===-1/0?c.stylize("-Infinity","number"):u===0?c.stylize(1/u===1/0?"+0":"-0","number"):c.stylize(V(u,c.truncate),"number")}function we(u,c){var g=V(u.toString(),c.truncate-1);return g!==M&&(g+="n"),c.stylize(g,"bigint")}function ke(u,c){var g=u.toString().split("/")[2],x=c.truncate-(2+g.length),E=u.source;return c.stylize("/".concat(V(E,x),"/").concat(g),"regexp")}function Ce(u){var c=[];return u.forEach(function(g){c.push(g)}),c}function $e(u,c){return u.size===0?"Set{}":(c.truncate-=7,"Set{ ".concat(B(Ce(u),c)," }"))}var a=new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]","g"),h={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","'":"\\'","\\":"\\\\"},p=16,y=4;function m(u){return h[u]||"\\u".concat("0000".concat(u.charCodeAt(0).toString(p)).slice(-y))}function w(u,c){return a.test(u)&&(u=u.replace(a,m)),c.stylize("'".concat(V(u,c.truncate-2),"'"),"string")}function b(u){return"description"in Symbol.prototype?u.description?"Symbol(".concat(u.description,")"):"Symbol()":u.toString()}var d=function(){return"Promise{\u2026}"};try{var S=process.binding("util"),A=S.getPromiseDetails,q=S.kPending,k=S.kRejected;Array.isArray(A(Promise.resolve()))&&(d=function(c,g){var x=A(c),E=t(x,2),N=E[0],D=E[1];return N===q?"Promise{}":"Promise".concat(N===k?"!":"","{").concat(g.inspect(D,g),"}")})}catch{}var j=d;function P(u,c){var g=Object.getOwnPropertyNames(u),x=Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(u):[];if(g.length===0&&x.length===0)return"{}";if(c.truncate-=4,c.seen=c.seen||[],c.seen.indexOf(u)>=0)return"[Circular]";c.seen.push(u);var E=B(g.map(function(z){return[z,u[z]]}),c,Z),N=B(x.map(function(z){return[z,u[z]]}),c,Z);c.seen.pop();var D="";return E&&N&&(D=", "),"{ ".concat(E).concat(D).concat(N," }")}var T=typeof Symbol<"u"&&Symbol.toStringTag?Symbol.toStringTag:!1;function L(u,c){var g="";return T&&T in u&&(g=u[T]),g=g||F(u.constructor),(!g||g==="_class")&&(g=""),c.truncate-=g.length,"".concat(g).concat(P(u,c))}function $(u,c){return u.length===0?"Arguments[]":(c.truncate-=13,"Arguments[ ".concat(B(u,c)," ]"))}var G=["stack","line","column","name","message","fileName","lineNumber","columnNumber","number","description"];function te(u,c){var g=Object.getOwnPropertyNames(u).filter(function(D){return G.indexOf(D)===-1}),x=u.name;c.truncate-=x.length;var E="";typeof u.message=="string"?E=V(u.message,c.truncate):g.unshift("message"),E=E?": ".concat(E):"",c.truncate-=E.length+5;var N=B(g.map(function(D){return[D,u[D]]}),c,Z);return"".concat(x).concat(E).concat(N?" { ".concat(N," }"):"")}function pr(u,c){var g=t(u,2),x=g[0],E=g[1];return c.truncate-=3,E?"".concat(c.stylize(x,"yellow"),"=").concat(c.stylize('"'.concat(E,'"'),"string")):"".concat(c.stylize(x,"yellow"))}function _e(u,c){return B(u,c,lt,` +`)}function lt(u,c){var g=u.getAttributeNames(),x=u.tagName.toLowerCase(),E=c.stylize("<".concat(x),"special"),N=c.stylize(">","special"),D=c.stylize(""),"special");c.truncate-=x.length*2+5;var z="";g.length>0&&(z+=" ",z+=B(g.map(function(Q){return[Q,u.getAttribute(Q)]}),c,pr," ")),c.truncate-=z.length;var _=c.truncate,U=_e(u.children,c);return U&&U.length>_&&(U="".concat(M,"(").concat(u.children.length,")")),"".concat(E).concat(z).concat(N).concat(U).concat(D)}var gr=typeof Symbol=="function"&&typeof Symbol.for=="function",ze=gr?Symbol.for("chai/inspect"):"@@chai/inspect",ye=!1;try{var ht=It();ye=ht.inspect?ht.inspect.custom:!1}catch{ye=!1}function dt(){this.key="chai/loupe__"+Math.random()+Date.now()}dt.prototype={get:function(c){return c[this.key]},has:function(c){return this.key in c},set:function(c,g){Object.isExtensible(c)&&Object.defineProperty(c,this.key,{value:g,configurable:!0})}};var Be=new(typeof WeakMap=="function"?WeakMap:dt),Fe={},pt={undefined:function(c,g){return g.stylize("undefined","undefined")},null:function(c,g){return g.stylize(null,"null")},boolean:function(c,g){return g.stylize(c,"boolean")},Boolean:function(c,g){return g.stylize(c,"boolean")},number:Ie,Number:Ie,bigint:we,BigInt:we,string:w,String:w,function:ge,Function:ge,symbol:b,Symbol:b,Array:le,Date:R,Map:De,Set:$e,RegExp:ke,Promise:j,WeakSet:function(c,g){return g.stylize("WeakSet{\u2026}","special")},WeakMap:function(c,g){return g.stylize("WeakMap{\u2026}","special")},Arguments:$,Int8Array:W,Uint8Array:W,Uint8ClampedArray:W,Int16Array:W,Uint16Array:W,Int32Array:W,Uint32Array:W,Float32Array:W,Float64Array:W,Generator:function(){return""},DataView:function(){return""},ArrayBuffer:function(){return""},Error:te,HTMLCollection:_e,NodeList:_e},yr=function(c,g,x){return ze in c&&typeof c[ze]=="function"?c[ze](g):ye&&ye in c&&typeof c[ye]=="function"?c[ye](g.depth,g):"inspect"in c&&typeof c.inspect=="function"?c.inspect(g.depth,g):"constructor"in c&&Be.has(c.constructor)?Be.get(c.constructor)(c,g):Fe[x]?Fe[x](c,g):""},br=Object.prototype.toString;function Ve(u,c){c=H(c),c.inspect=Ve;var g=c,x=g.customInspect,E=u===null?"null":o(u);if(E==="object"&&(E=br.call(u).slice(8,-1)),pt[E])return pt[E](u,c);if(x&&u){var N=yr(u,c,E);if(N)return typeof N=="string"?N:Ve(N,c)}var D=u?Object.getPrototypeOf(u):!1;return D===Object.prototype||D===null?P(u,c):u&&typeof HTMLElement=="function"&&u instanceof HTMLElement?lt(u,c):"constructor"in u?u.constructor!==Object?L(u,c):P(u,c):u===Object(u)?P(u,c):c.stylize(String(u),E)}function mr(u,c){return Be.has(u)?!1:(Be.set(u,c),!0)}function vr(u,c){return u in Fe?!1:(Fe[u]=c,!0)}var wr=ze;s.custom=wr,s.default=Ve,s.inspect=Ve,s.registerConstructor=mr,s.registerStringTag=vr,Object.defineProperty(s,"__esModule",{value:!0})})});var ce=O((ai,zt)=>{zt.exports={includeStack:!1,showDiff:!0,truncateThreshold:40,useProxy:!0,proxyExcludedKeys:["then","catch","inspect","toJSON"]}});var Ke=O((ci,Ft)=>{var ui=tt(),Lr=Ct(),Bt=ce();Ft.exports=Kr;function Kr(s,o,t,f){var e={colors:f,depth:typeof t>"u"?2:t,showHidden:o,truncate:Bt.truncateThreshold?Bt.truncateThreshold:1/0};return Lr.inspect(s,e)}});var nt=O((fi,Rt)=>{var Gr=Ke(),Vt=ce();Rt.exports=function(o){var t=Gr(o),f=Object.prototype.toString.call(o);if(Vt.truncateThreshold&&t.length>=Vt.truncateThreshold){if(f==="[object Function]")return!o.name||o.name===""?"[Function]":"[Function: "+o.name+"]";if(f==="[object Array]")return"[ Array("+o.length+") ]";if(f==="[object Object]"){var e=Object.keys(o),n=e.length>2?e.splice(0,2).join(", ")+", ...":e.join(", ");return"{ Object ("+n+") }"}else return t}else return t}});var Kt=O((li,Lt)=>{var rt=Y(),Ur=et(),ot=nt();Lt.exports=function(o,t){var f=rt(o,"negate"),e=rt(o,"object"),n=t[3],r=Ur(o,t),i=f?t[2]:t[1],l=rt(o,"message");return typeof i=="function"&&(i=i()),i=i||"",i=i.replace(/#\{this\}/g,function(){return ot(e)}).replace(/#\{act\}/g,function(){return ot(r)}).replace(/#\{exp\}/g,function(){return ot(n)}),l?l+": "+i:i}});var re=O((hi,Gt)=>{Gt.exports=function(o,t,f){var e=o.__flags||(o.__flags=Object.create(null));t.__flags||(t.__flags=Object.create(null)),f=arguments.length===3?f:!0;for(var n in e)(f||n!=="object"&&n!=="ssfi"&&n!=="lockSsfi"&&n!="message")&&(t.__flags[n]=e[n])}});var tn=O((di,at)=>{"use strict";var Ut=Me();function Xt(){this._key="chai/deep-eql__"+Math.random()+Date.now()}Xt.prototype={get:function(o){return o[this._key]},set:function(o,t){Object.isExtensible(o)&&Object.defineProperty(o,this._key,{value:t,configurable:!0})}};var st=typeof WeakMap=="function"?WeakMap:Xt;function Wt(s,o,t){if(!t||me(s)||me(o))return null;var f=t.get(s);if(f){var e=f.get(o);if(typeof e=="boolean")return e}return null}function Ge(s,o,t,f){if(!(!t||me(s)||me(o))){var e=t.get(s);e?e.set(o,f):(e=new st,e.set(o,f),t.set(s,e))}}at.exports=Ue;at.exports.MemoizeMap=st;function Ue(s,o,t){if(t&&t.comparator)return $t(s,o,t);var f=Ht(s,o);return f!==null?f:$t(s,o,t)}function Ht(s,o){return s===o?s!==0||1/s===1/o:s!==s&&o!==o?!0:me(s)||me(o)?!1:null}function $t(s,o,t){t=t||{},t.memoize=t.memoize===!1?!1:t.memoize||new st;var f=t&&t.comparator,e=Wt(s,o,t.memoize);if(e!==null)return e;var n=Wt(o,s,t.memoize);if(n!==null)return n;if(f){var r=f(s,o);if(r===!1||r===!0)return Ge(s,o,t.memoize,r),r;var i=Ht(s,o);if(i!==null)return i}var l=Ut(s);if(l!==Ut(o))return Ge(s,o,t.memoize,!1),!1;Ge(s,o,t.memoize,!0);var v=Wr(s,o,l,t);return Ge(s,o,t.memoize,v),v}function Wr(s,o,t,f){switch(t){case"String":case"Number":case"Boolean":case"Date":return Ue(s.valueOf(),o.valueOf());case"Promise":case"Symbol":case"function":case"WeakMap":case"WeakSet":return s===o;case"Error":return en(s,o,["name","message","code"],f);case"Arguments":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"Array":return fe(s,o,f);case"RegExp":return $r(s,o);case"Generator":return _r(s,o,f);case"DataView":return fe(new Uint8Array(s.buffer),new Uint8Array(o.buffer),f);case"ArrayBuffer":return fe(new Uint8Array(s),new Uint8Array(o),f);case"Set":return _t(s,o,f);case"Map":return _t(s,o,f);case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.Instant":case"Temporal.ZonedDateTime":case"Temporal.PlainYearMonth":case"Temporal.PlainMonthDay":return s.equals(o);case"Temporal.Duration":return s.total("nanoseconds")===o.total("nanoseconds");case"Temporal.TimeZone":case"Temporal.Calendar":return s.toString()===o.toString();default:return Zr(s,o,f)}}function $r(s,o){return s.toString()===o.toString()}function _t(s,o,t){if(s.size!==o.size)return!1;if(s.size===0)return!0;var f=[],e=[];return s.forEach(function(r,i){f.push([r,i])}),o.forEach(function(r,i){e.push([r,i])}),fe(f.sort(),e.sort(),t)}function fe(s,o,t){var f=s.length;if(f!==o.length)return!1;if(f===0)return!0;for(var e=-1;++e{var Qr=ce();nn.exports=function(){return Qr.useProxy&&typeof Proxy<"u"&&typeof Reflect<"u"}});var sn=O((gi,on)=>{var Yr=oe(),rn=Y(),Xr=Ee(),Hr=re();on.exports=function(o,t,f){f=f===void 0?function(){}:f,Object.defineProperty(o,t,{get:function e(){!Xr()&&!rn(this,"lockSsfi")&&rn(this,"ssfi",e);var n=f.call(this);if(n!==void 0)return n;var r=new Yr.Assertion;return Hr(this,r),r},configurable:!0})}});var Ae=O((yi,an)=>{var eo=Object.getOwnPropertyDescriptor(function(){},"length");an.exports=function(o,t,f){return eo.configurable&&Object.defineProperty(o,"length",{get:function(){throw Error(f?"Invalid Chai property: "+t+'.length. Due to a compatibility issue, "length" cannot directly follow "'+t+'". Use "'+t+'.lengthOf" instead.':"Invalid Chai property: "+t+'.length. See docs for proper usage of "'+t+'".')}}),o}});var cn=O((bi,un)=>{un.exports=function(o){var t=Object.getOwnPropertyNames(o);function f(n){t.indexOf(n)===-1&&t.push(n)}for(var e=Object.getPrototypeOf(o);e!==null;)Object.getOwnPropertyNames(e).forEach(f),e=Object.getPrototypeOf(e);return t}});var Pe=O((mi,hn)=>{var to=ce(),fn=Y(),no=cn(),ro=Ee();var ln=["__flags","__methods","_obj","assert"];hn.exports=function(o,t){return ro()?new Proxy(o,{get:function f(e,n){if(typeof n=="string"&&to.proxyExcludedKeys.indexOf(n)===-1&&!Reflect.has(e,n)){if(t)throw Error("Invalid Chai property: "+t+"."+n+'. See docs for proper usage of "'+t+'".');var r=null,i=4;throw no(e).forEach(function(l){if(!Object.prototype.hasOwnProperty(l)&&ln.indexOf(l)===-1){var v=oo(n,l,i);v=t)return t;for(var f=[],e=0;e<=s.length;e++)f[e]=Array(o.length+1).fill(0),f[e][0]=e;for(var n=0;n=t){f[e][n]=t;continue}f[e][n]=Math.min(f[e-1][n]+1,f[e][n-1]+1,f[e-1][n-1]+(r===o.charCodeAt(n-1)?0:1))}return f[s.length][o.length]}});var gn=O((vi,pn)=>{var io=Ae(),so=oe(),dn=Y(),ao=Pe(),uo=re();pn.exports=function(o,t,f){var e=function(){dn(this,"lockSsfi")||dn(this,"ssfi",e);var n=f.apply(this,arguments);if(n!==void 0)return n;var r=new so.Assertion;return uo(this,r),r};io(e,t,!1),o[t]=ao(e,t)}});var bn=O((wi,yn)=>{var co=oe(),Oe=Y(),fo=Ee(),lo=re();yn.exports=function(o,t,f){var e=Object.getOwnPropertyDescriptor(o,t),n=function(){};e&&typeof e.get=="function"&&(n=e.get),Object.defineProperty(o,t,{get:function r(){!fo()&&!Oe(this,"lockSsfi")&&Oe(this,"ssfi",r);var i=Oe(this,"lockSsfi");Oe(this,"lockSsfi",!0);var l=f(n).call(this);if(Oe(this,"lockSsfi",i),l!==void 0)return l;var v=new co.Assertion;return lo(this,v),v},configurable:!0})}});var vn=O((xi,mn)=>{var ho=Ae(),po=oe(),Ne=Y(),go=Pe(),yo=re();mn.exports=function(o,t,f){var e=o[t],n=function(){throw new Error(t+" is not a function")};e&&typeof e=="function"&&(n=e);var r=function(){Ne(this,"lockSsfi")||Ne(this,"ssfi",r);var i=Ne(this,"lockSsfi");Ne(this,"lockSsfi",!0);var l=f(n).apply(this,arguments);if(Ne(this,"lockSsfi",i),l!==void 0)return l;var v=new po.Assertion;return yo(this,v),v};ho(r,t,!1),o[t]=go(r,t)}});var En=O((Si,Mn)=>{var bo=Ae(),mo=oe(),wn=Y(),vo=Pe(),xn=re();var wo=typeof Object.setPrototypeOf=="function",Sn=function(){},xo=Object.getOwnPropertyNames(Sn).filter(function(s){var o=Object.getOwnPropertyDescriptor(Sn,s);return typeof o!="object"?!0:!o.configurable}),So=Function.prototype.call,Mo=Function.prototype.apply;Mn.exports=function(o,t,f,e){typeof e!="function"&&(e=function(){});var n={method:f,chainingBehavior:e};o.__methods||(o.__methods={}),o.__methods[t]=n,Object.defineProperty(o,t,{get:function(){n.chainingBehavior.call(this);var i=function(){wn(this,"lockSsfi")||wn(this,"ssfi",i);var M=n.method.apply(this,arguments);if(M!==void 0)return M;var K=new mo.Assertion;return xn(this,K),K};if(bo(i,t,!0),wo){var l=Object.create(this);l.call=So,l.apply=Mo,Object.setPrototypeOf(i,l)}else{var v=Object.getOwnPropertyNames(o);v.forEach(function(M){if(xo.indexOf(M)===-1){var K=Object.getOwnPropertyDescriptor(o,M);Object.defineProperty(i,M,K)}})}return xn(this,i),vo(i)},configurable:!0})}});var Nn=O((Mi,On)=>{var An=oe(),Pn=re();On.exports=function(o,t,f,e){var n=o.__methods[t],r=n.chainingBehavior;n.chainingBehavior=function(){var v=e(r).call(this);if(v!==void 0)return v;var M=new An.Assertion;return Pn(this,M),M};var i=n.method;n.method=function(){var v=f(i).apply(this,arguments);if(v!==void 0)return v;var M=new An.Assertion;return Pn(this,M),M}}});var Tn=O((Ei,qn)=>{var jn=Ke();qn.exports=function(o,t){return jn(o){Dn.exports=function(o){return typeof Object.getOwnPropertySymbols!="function"?[]:Object.getOwnPropertySymbols(o).filter(function(t){return Object.getOwnPropertyDescriptor(o,t).enumerable})}});var kn=O((Pi,In)=>{var Eo=ut();In.exports=function(o){return Object.keys(o).concat(Eo(o))}});var zn=O((Oi,Cn)=>{"use strict";function Ao(s,o){return o instanceof Error&&s===o}function Po(s,o){return o instanceof Error?s.constructor===o.constructor||s instanceof o.constructor:o.prototype instanceof Error||o===Error?s.constructor===o||s instanceof o:!1}function Oo(s,o){var t=typeof s=="string"?s:s.message;return o instanceof RegExp?o.test(t):typeof o=="string"?t.indexOf(o)!==-1:!1}var No=/\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/;function ct(s){var o="";if(typeof s.name>"u"){var t=String(s).match(No);t&&(o=t[1])}else o=s.name;return o}function jo(s){var o=s;return s instanceof Error?o=ct(s.constructor):typeof s=="function"&&(o=ct(s).trim()||ct(new s)),o}function qo(s){var o="";return s&&s.message?o=s.message:typeof s=="string"&&(o=s),o}Cn.exports={compatibleInstance:Ao,compatibleConstructor:Po,compatibleMessage:Oo,getMessage:qo,getConstructorName:jo}});var Fn=O((Ni,Bn)=>{function To(s){return s!==s}Bn.exports=Number.isNaN||To});var Ln=O((ji,Rn)=>{var Do=Me(),Vn=Y();function Io(s){var o=Do(s),t=["Array","Object","function"];return t.indexOf(o)!==-1}Rn.exports=function(o,t){var f=Vn(o,"operator"),e=Vn(o,"negate"),n=t[3],r=e?t[2]:t[1];if(f)return f;if(typeof r=="function"&&(r=r()),r=r||"",!!r&&!/\shave\s/.test(r)){var i=Io(n);return/\snot\s/.test(r)?i?"notDeepStrictEqual":"notStrictEqual":i?"deepStrictEqual":"strictEqual"}}});var Gn=O(I=>{var Kn=At();I.test=Nt();I.type=Me();I.expectTypes=qt();I.getMessage=Kt();I.getActual=et();I.inspect=Ke();I.objDisplay=nt();I.flag=Y();I.transferFlags=re();I.eql=tn();I.getPathInfo=Kn.getPathInfo;I.hasProperty=Kn.hasProperty;I.getName=tt();I.addProperty=sn();I.addMethod=gn();I.overwriteProperty=bn();I.overwriteMethod=vn();I.addChainableMethod=En();I.overwriteChainableMethod=Nn();I.compareByInspect=Tn();I.getOwnEnumerablePropertySymbols=ut();I.getOwnEnumerableProperties=kn();I.checkError=zn();I.proxify=Pe();I.addLengthGuard=Ae();I.isProxyEnabled=Ee();I.isNaN=Fn();I.getOperator=Ln()});var Wn=O((Ti,Un)=>{var ve=ce();Un.exports=function(s,o){var t=s.AssertionError,f=o.flag;s.Assertion=e;function e(n,r,i,l){return f(this,"ssfi",i||e),f(this,"lockSsfi",l),f(this,"object",n),f(this,"message",r),o.proxify(this)}Object.defineProperty(e,"includeStack",{get:function(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),ve.includeStack},set:function(n){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),ve.includeStack=n}}),Object.defineProperty(e,"showDiff",{get:function(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),ve.showDiff},set:function(n){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),ve.showDiff=n}}),e.addProperty=function(n,r){o.addProperty(this.prototype,n,r)},e.addMethod=function(n,r){o.addMethod(this.prototype,n,r)},e.addChainableMethod=function(n,r,i){o.addChainableMethod(this.prototype,n,r,i)},e.overwriteProperty=function(n,r){o.overwriteProperty(this.prototype,n,r)},e.overwriteMethod=function(n,r){o.overwriteMethod(this.prototype,n,r)},e.overwriteChainableMethod=function(n,r,i){o.overwriteChainableMethod(this.prototype,n,r,i)},e.prototype.assert=function(n,r,i,l,v,M){var K=o.test(this,arguments);if(M!==!1&&(M=!0),l===void 0&&v===void 0&&(M=!1),ve.showDiff!==!0&&(M=!1),!K){r=o.getMessage(this,arguments);var H=o.getActual(this,arguments),V={actual:H,expected:l,showDiff:M},B=o.getOperator(this,arguments);throw B&&(V.operator=B),new t(r,V,ve.includeStack?this.assert:f(this,"ssfi"))}};Object.defineProperty(e.prototype,"_obj",{get:function(){return f(this,"object")},set:function(n){f(this,"object",n)}})}});var _n=O((Di,$n)=>{$n.exports=function(s,o){var t=s.Assertion,f=s.AssertionError,e=o.flag;["to","be","been","is","and","has","have","with","that","which","at","of","same","but","does","still","also"].forEach(function(a){t.addProperty(a)}),t.addProperty("not",function(){e(this,"negate",!0)}),t.addProperty("deep",function(){e(this,"deep",!0)}),t.addProperty("nested",function(){e(this,"nested",!0)}),t.addProperty("own",function(){e(this,"own",!0)}),t.addProperty("ordered",function(){e(this,"ordered",!0)}),t.addProperty("any",function(){e(this,"any",!0),e(this,"all",!1)}),t.addProperty("all",function(){e(this,"all",!0),e(this,"any",!1)});function n(a,h){h&&e(this,"message",h),a=a.toLowerCase();var p=e(this,"object"),y=~["a","e","i","o","u"].indexOf(a.charAt(0))?"an ":"a ";this.assert(a===o.type(p).toLowerCase(),"expected #{this} to be "+y+a,"expected #{this} not to be "+y+a)}t.addChainableMethod("an",n),t.addChainableMethod("a",n);function r(a,h){return o.isNaN(a)&&o.isNaN(h)||a===h}function i(){e(this,"contains",!0)}function l(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=o.type(p).toLowerCase(),m=e(this,"message"),w=e(this,"negate"),b=e(this,"ssfi"),d=e(this,"deep"),S=d?"deep ":"";m=m?m+": ":"";var A=!1;switch(y){case"string":A=p.indexOf(a)!==-1;break;case"weakset":if(d)throw new f(m+"unable to use .deep.include with WeakSet",void 0,b);A=p.has(a);break;case"map":var q=d?o.eql:r;p.forEach(function(T){A=A||q(T,a)});break;case"set":d?p.forEach(function(T){A=A||o.eql(T,a)}):A=p.has(a);break;case"array":d?A=p.some(function(T){return o.eql(T,a)}):A=p.indexOf(a)!==-1;break;default:if(a!==Object(a))throw new f(m+"the given combination of arguments ("+y+" and "+o.type(a).toLowerCase()+") is invalid for this assertion. You can use an array, a map, an object, a set, a string, or a weakset instead of a "+o.type(a).toLowerCase(),void 0,b);var k=Object.keys(a),j=null,P=0;if(k.forEach(function(T){var L=new t(p);if(o.transferFlags(this,L,!0),e(L,"lockSsfi",!0),!w||k.length===1){L.property(T,a[T]);return}try{L.property(T,a[T])}catch($){if(!o.checkError.compatibleConstructor($,f))throw $;j===null&&(j=$),P++}},this),w&&k.length>1&&P===k.length)throw j;return}this.assert(A,"expected #{this} to "+S+"include "+o.inspect(a),"expected #{this} to not "+S+"include "+o.inspect(a))}t.addChainableMethod("include",l,i),t.addChainableMethod("contain",l,i),t.addChainableMethod("contains",l,i),t.addChainableMethod("includes",l,i),t.addProperty("ok",function(){this.assert(e(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")}),t.addProperty("true",function(){this.assert(e(this,"object")===!0,"expected #{this} to be true","expected #{this} to be false",!e(this,"negate"))}),t.addProperty("false",function(){this.assert(e(this,"object")===!1,"expected #{this} to be false","expected #{this} to be true",!!e(this,"negate"))}),t.addProperty("null",function(){this.assert(e(this,"object")===null,"expected #{this} to be null","expected #{this} not to be null")}),t.addProperty("undefined",function(){this.assert(e(this,"object")===void 0,"expected #{this} to be undefined","expected #{this} not to be undefined")}),t.addProperty("NaN",function(){this.assert(o.isNaN(e(this,"object")),"expected #{this} to be NaN","expected #{this} not to be NaN")});function v(){var a=e(this,"object");this.assert(a!=null,"expected #{this} to exist","expected #{this} to not exist")}t.addProperty("exist",v),t.addProperty("exists",v),t.addProperty("empty",function(){var a=e(this,"object"),h=e(this,"ssfi"),p=e(this,"message"),y;switch(p=p?p+": ":"",o.type(a).toLowerCase()){case"array":case"string":y=a.length;break;case"map":case"set":y=a.size;break;case"weakmap":case"weakset":throw new f(p+".empty was passed a weak collection",void 0,h);case"function":var m=p+".empty was passed a function "+o.getName(a);throw new f(m.trim(),void 0,h);default:if(a!==Object(a))throw new f(p+".empty was passed non-string primitive "+o.inspect(a),void 0,h);y=Object.keys(a).length}this.assert(y===0,"expected #{this} to be empty","expected #{this} not to be empty")});function M(){var a=e(this,"object"),h=o.type(a);this.assert(h==="Arguments","expected #{this} to be arguments but got "+h,"expected #{this} to not be arguments")}t.addProperty("arguments",M),t.addProperty("Arguments",M);function K(a,h){h&&e(this,"message",h);var p=e(this,"object");if(e(this,"deep")){var y=e(this,"lockSsfi");e(this,"lockSsfi",!0),this.eql(a),e(this,"lockSsfi",y)}else this.assert(a===p,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",a,this._obj,!0)}t.addMethod("equal",K),t.addMethod("equals",K),t.addMethod("eq",K);function H(a,h){h&&e(this,"message",h),this.assert(o.eql(a,e(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",a,this._obj,!0)}t.addMethod("eql",H),t.addMethod("eqls",H);function V(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"doLength"),m=e(this,"message"),w=m?m+": ":"",b=e(this,"ssfi"),d=o.type(p).toLowerCase(),S=o.type(a).toLowerCase(),A,q=!0;if(y&&d!=="map"&&d!=="set"&&new t(p,m,b,!0).to.have.property("length"),!y&&d==="date"&&S!=="date")A=w+"the argument to above must be a date";else if(S!=="number"&&(y||d==="number"))A=w+"the argument to above must be a number";else if(!y&&d!=="date"&&d!=="number"){var k=d==="string"?"'"+p+"'":p;A=w+"expected "+k+" to be a number or a date"}else q=!1;if(q)throw new f(A,void 0,b);if(y){var j="length",P;d==="map"||d==="set"?(j="size",P=p.size):P=p.length,this.assert(P>a,"expected #{this} to have a "+j+" above #{exp} but got #{act}","expected #{this} to not have a "+j+" above #{exp}",a,P)}else this.assert(p>a,"expected #{this} to be above #{exp}","expected #{this} to be at most #{exp}",a)}t.addMethod("above",V),t.addMethod("gt",V),t.addMethod("greaterThan",V);function B(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"doLength"),m=e(this,"message"),w=m?m+": ":"",b=e(this,"ssfi"),d=o.type(p).toLowerCase(),S=o.type(a).toLowerCase(),A,q=!0;if(y&&d!=="map"&&d!=="set"&&new t(p,m,b,!0).to.have.property("length"),!y&&d==="date"&&S!=="date")A=w+"the argument to least must be a date";else if(S!=="number"&&(y||d==="number"))A=w+"the argument to least must be a number";else if(!y&&d!=="date"&&d!=="number"){var k=d==="string"?"'"+p+"'":p;A=w+"expected "+k+" to be a number or a date"}else q=!1;if(q)throw new f(A,void 0,b);if(y){var j="length",P;d==="map"||d==="set"?(j="size",P=p.size):P=p.length,this.assert(P>=a,"expected #{this} to have a "+j+" at least #{exp} but got #{act}","expected #{this} to have a "+j+" below #{exp}",a,P)}else this.assert(p>=a,"expected #{this} to be at least #{exp}","expected #{this} to be below #{exp}",a)}t.addMethod("least",B),t.addMethod("gte",B),t.addMethod("greaterThanOrEqual",B);function ee(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"doLength"),m=e(this,"message"),w=m?m+": ":"",b=e(this,"ssfi"),d=o.type(p).toLowerCase(),S=o.type(a).toLowerCase(),A,q=!0;if(y&&d!=="map"&&d!=="set"&&new t(p,m,b,!0).to.have.property("length"),!y&&d==="date"&&S!=="date")A=w+"the argument to below must be a date";else if(S!=="number"&&(y||d==="number"))A=w+"the argument to below must be a number";else if(!y&&d!=="date"&&d!=="number"){var k=d==="string"?"'"+p+"'":p;A=w+"expected "+k+" to be a number or a date"}else q=!1;if(q)throw new f(A,void 0,b);if(y){var j="length",P;d==="map"||d==="set"?(j="size",P=p.size):P=p.length,this.assert(P=a&&$<=h,"expected #{this} to have a "+L+" within "+P,"expected #{this} to not have a "+L+" within "+P)}else this.assert(y>=a&&y<=h,"expected #{this} to be within "+P,"expected #{this} to not be within "+P)});function le(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"ssfi"),m=e(this,"message");try{var w=p instanceof a}catch(d){throw d instanceof TypeError?(m=m?m+": ":"",new f(m+"The instanceof assertion needs a constructor but "+o.type(a)+" was given.",void 0,y)):d}var b=o.getName(a);b===null&&(b="an unnamed constructor"),this.assert(w,"expected #{this} to be an instance of "+b,"expected #{this} to not be an instance of "+b)}t.addMethod("instanceof",le),t.addMethod("instanceOf",le);function he(a,h,p){p&&e(this,"message",p);var y=e(this,"nested"),m=e(this,"own"),w=e(this,"message"),b=e(this,"object"),d=e(this,"ssfi"),S=typeof a;if(w=w?w+": ":"",y){if(S!=="string")throw new f(w+"the argument to property must be a string when using nested syntax",void 0,d)}else if(S!=="string"&&S!=="number"&&S!=="symbol")throw new f(w+"the argument to property must be a string, number, or symbol",void 0,d);if(y&&m)throw new f(w+'The "nested" and "own" flags cannot be combined.',void 0,d);if(b==null)throw new f(w+"Target cannot be null or undefined.",void 0,d);var A=e(this,"deep"),q=e(this,"negate"),k=y?o.getPathInfo(b,a):null,j=y?k.value:b[a],P="";A&&(P+="deep "),m&&(P+="own "),y&&(P+="nested "),P+="property ";var T;m?T=Object.prototype.hasOwnProperty.call(b,a):y?T=k.exists:T=o.hasProperty(b,a),(!q||arguments.length===1)&&this.assert(T,"expected #{this} to have "+P+o.inspect(a),"expected #{this} to not have "+P+o.inspect(a)),arguments.length>1&&this.assert(T&&(A?o.eql(h,j):h===j),"expected #{this} to have "+P+o.inspect(a)+" of #{exp}, but got #{act}","expected #{this} to not have "+P+o.inspect(a)+" of #{act}",h,j),e(this,"object",j)}t.addMethod("property",he);function de(a,h,p){e(this,"own",!0),he.apply(this,arguments)}t.addMethod("ownProperty",de),t.addMethod("haveOwnProperty",de);function pe(a,h,p){typeof h=="string"&&(p=h,h=null),p&&e(this,"message",p);var y=e(this,"object"),m=Object.getOwnPropertyDescriptor(Object(y),a);m&&h?this.assert(o.eql(h,m),"expected the own property descriptor for "+o.inspect(a)+" on #{this} to match "+o.inspect(h)+", got "+o.inspect(m),"expected the own property descriptor for "+o.inspect(a)+" on #{this} to not match "+o.inspect(h),h,m,!0):this.assert(m,"expected #{this} to have an own property descriptor for "+o.inspect(a),"expected #{this} to not have an own property descriptor for "+o.inspect(a)),e(this,"object",m)}t.addMethod("ownPropertyDescriptor",pe),t.addMethod("haveOwnPropertyDescriptor",pe);function F(){e(this,"doLength",!0)}function se(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=o.type(p).toLowerCase(),m=e(this,"message"),w=e(this,"ssfi"),b="length",d;switch(y){case"map":case"set":b="size",d=p.size;break;default:new t(p,m,w,!0).to.have.property("length"),d=p.length}this.assert(d==a,"expected #{this} to have a "+b+" of #{exp} but got #{act}","expected #{this} to not have a "+b+" of #{act}",a,d)}t.addChainableMethod("length",se,F),t.addChainableMethod("lengthOf",se,F);function W(a,h){h&&e(this,"message",h);var p=e(this,"object");this.assert(a.exec(p),"expected #{this} to match "+a,"expected #{this} not to match "+a)}t.addMethod("match",W),t.addMethod("matches",W),t.addMethod("string",function(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"message"),m=e(this,"ssfi");new t(p,y,m,!0).is.a("string"),this.assert(~p.indexOf(a),"expected #{this} to contain "+o.inspect(a),"expected #{this} to not contain "+o.inspect(a))});function R(a){var h=e(this,"object"),p=o.type(h),y=o.type(a),m=e(this,"ssfi"),w=e(this,"deep"),b,d="",S,A=!0,q=e(this,"message");q=q?q+": ":"";var k=q+"when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments";if(p==="Map"||p==="Set")d=w?"deeply ":"",S=[],h.forEach(function(G,te){S.push(te)}),y!=="Array"&&(a=Array.prototype.slice.call(arguments));else{switch(S=o.getOwnEnumerableProperties(h),y){case"Array":if(arguments.length>1)throw new f(k,void 0,m);break;case"Object":if(arguments.length>1)throw new f(k,void 0,m);a=Object.keys(a);break;default:a=Array.prototype.slice.call(arguments)}a=a.map(function(G){return typeof G=="symbol"?G:String(G)})}if(!a.length)throw new f(q+"keys required",void 0,m);var j=a.length,P=e(this,"any"),T=e(this,"all"),L=a;if(!P&&!T&&(T=!0),P&&(A=L.some(function(G){return S.some(function(te){return w?o.eql(G,te):G===te})})),T&&(A=L.every(function(G){return S.some(function(te){return w?o.eql(G,te):G===te})}),e(this,"contains")||(A=A&&a.length==S.length)),j>1){a=a.map(function(G){return o.inspect(G)});var $=a.pop();T&&(b=a.join(", ")+", and "+$),P&&(b=a.join(", ")+", or "+$)}else b=o.inspect(a[0]);b=(j>1?"keys ":"key ")+b,b=(e(this,"contains")?"contain ":"have ")+b,this.assert(A,"expected #{this} to "+d+b,"expected #{this} to not "+d+b,L.slice(0).sort(o.compareByInspect),S.sort(o.compareByInspect),!0)}t.addMethod("keys",R),t.addMethod("key",R);function ge(a,h,p){p&&e(this,"message",p);var y=e(this,"object"),m=e(this,"ssfi"),w=e(this,"message"),b=e(this,"negate")||!1;new t(y,w,m,!0).is.a("function"),(a instanceof RegExp||typeof a=="string")&&(h=a,a=null);var d;try{y()}catch(G){d=G}var S=a===void 0&&h===void 0,A=Boolean(a&&h),q=!1,k=!1;if(S||!S&&!b){var j="an error";a instanceof Error?j="#{exp}":a&&(j=o.checkError.getConstructorName(a)),this.assert(d,"expected #{this} to throw "+j,"expected #{this} to not throw an error but #{act} was thrown",a&&a.toString(),d instanceof Error?d.toString():typeof d=="string"?d:d&&o.checkError.getConstructorName(d))}if(a&&d){if(a instanceof Error){var P=o.checkError.compatibleInstance(d,a);P===b&&(A&&b?q=!0:this.assert(b,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(d&&!b?" but #{act} was thrown":""),a.toString(),d.toString()))}var T=o.checkError.compatibleConstructor(d,a);T===b&&(A&&b?q=!0:this.assert(b,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(d?" but #{act} was thrown":""),a instanceof Error?a.toString():a&&o.checkError.getConstructorName(a),d instanceof Error?d.toString():d&&o.checkError.getConstructorName(d)))}if(d&&h!==void 0&&h!==null){var L="including";h instanceof RegExp&&(L="matching");var $=o.checkError.compatibleMessage(d,h);$===b&&(A&&b?k=!0:this.assert(b,"expected #{this} to throw error "+L+" #{exp} but got #{act}","expected #{this} to throw error not "+L+" #{exp}",h,o.checkError.getMessage(d)))}q&&k&&this.assert(b,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(d?" but #{act} was thrown":""),a instanceof Error?a.toString():a&&o.checkError.getConstructorName(a),d instanceof Error?d.toString():d&&o.checkError.getConstructorName(d)),e(this,"object",d)}t.addMethod("throw",ge),t.addMethod("throws",ge),t.addMethod("Throw",ge);function qe(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"itself"),m=typeof p=="function"&&!y?p.prototype[a]:p[a];this.assert(typeof m=="function","expected #{this} to respond to "+o.inspect(a),"expected #{this} to not respond to "+o.inspect(a))}t.addMethod("respondTo",qe),t.addMethod("respondsTo",qe),t.addProperty("itself",function(){e(this,"itself",!0)});function Te(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=a(p);this.assert(y,"expected #{this} to satisfy "+o.objDisplay(a),"expected #{this} to not satisfy"+o.objDisplay(a),!e(this,"negate"),y)}t.addMethod("satisfy",Te),t.addMethod("satisfies",Te);function De(a,h,p){p&&e(this,"message",p);var y=e(this,"object"),m=e(this,"message"),w=e(this,"ssfi");if(new t(y,m,w,!0).is.a("number"),typeof a!="number"||typeof h!="number"){m=m?m+": ":"";var b=h===void 0?", and a delta is required":"";throw new f(m+"the arguments to closeTo or approximately must be numbers"+b,void 0,w)}this.assert(Math.abs(y-a)<=h,"expected #{this} to be close to "+a+" +/- "+h,"expected #{this} not to be close to "+a+" +/- "+h)}t.addMethod("closeTo",De),t.addMethod("approximately",De);function We(a,h,p,y,m){if(!y){if(a.length!==h.length)return!1;h=h.slice()}return a.every(function(w,b){if(m)return p?p(w,h[b]):w===h[b];if(!p){var d=h.indexOf(w);return d===-1?!1:(y||h.splice(d,1),!0)}return h.some(function(S,A){return p(w,S)?(y||h.splice(A,1),!0):!1})})}t.addMethod("members",function(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"message"),m=e(this,"ssfi");new t(p,y,m,!0).to.be.an("array"),new t(a,y,m,!0).to.be.an("array");var w=e(this,"contains"),b=e(this,"ordered"),d,S,A;w?(d=b?"an ordered superset":"a superset",S="expected #{this} to be "+d+" of #{exp}",A="expected #{this} to not be "+d+" of #{exp}"):(d=b?"ordered members":"members",S="expected #{this} to have the same "+d+" as #{exp}",A="expected #{this} to not have the same "+d+" as #{exp}");var q=e(this,"deep")?o.eql:void 0;this.assert(We(a,p,q,w,b),S,A,a,p,!0)});function Ie(a,h){h&&e(this,"message",h);var p=e(this,"object"),y=e(this,"message"),m=e(this,"ssfi"),w=e(this,"contains"),b=e(this,"deep");new t(a,y,m,!0).to.be.an("array"),w?this.assert(a.some(function(d){return p.indexOf(d)>-1}),"expected #{this} to contain one of #{exp}","expected #{this} to not contain one of #{exp}",a,p):b?this.assert(a.some(function(d){return o.eql(p,d)}),"expected #{this} to deeply equal one of #{exp}","expected #{this} to deeply equal one of #{exp}",a,p):this.assert(a.indexOf(p)>-1,"expected #{this} to be one of #{exp}","expected #{this} to not be one of #{exp}",a,p)}t.addMethod("oneOf",Ie);function we(a,h,p){p&&e(this,"message",p);var y=e(this,"object"),m=e(this,"message"),w=e(this,"ssfi");new t(y,m,w,!0).is.a("function");var b;h?(new t(a,m,w,!0).to.have.property(h),b=a[h]):(new t(a,m,w,!0).is.a("function"),b=a()),y();var d=h==null?a():a[h],S=h==null?b:"."+h;e(this,"deltaMsgObj",S),e(this,"initialDeltaValue",b),e(this,"finalDeltaValue",d),e(this,"deltaBehavior","change"),e(this,"realDelta",d!==b),this.assert(b!==d,"expected "+S+" to change","expected "+S+" to not change")}t.addMethod("change",we),t.addMethod("changes",we);function ke(a,h,p){p&&e(this,"message",p);var y=e(this,"object"),m=e(this,"message"),w=e(this,"ssfi");new t(y,m,w,!0).is.a("function");var b;h?(new t(a,m,w,!0).to.have.property(h),b=a[h]):(new t(a,m,w,!0).is.a("function"),b=a()),new t(b,m,w,!0).is.a("number"),y();var d=h==null?a():a[h],S=h==null?b:"."+h;e(this,"deltaMsgObj",S),e(this,"initialDeltaValue",b),e(this,"finalDeltaValue",d),e(this,"deltaBehavior","increase"),e(this,"realDelta",d-b),this.assert(d-b>0,"expected "+S+" to increase","expected "+S+" to not increase")}t.addMethod("increase",ke),t.addMethod("increases",ke);function Ce(a,h,p){p&&e(this,"message",p);var y=e(this,"object"),m=e(this,"message"),w=e(this,"ssfi");new t(y,m,w,!0).is.a("function");var b;h?(new t(a,m,w,!0).to.have.property(h),b=a[h]):(new t(a,m,w,!0).is.a("function"),b=a()),new t(b,m,w,!0).is.a("number"),y();var d=h==null?a():a[h],S=h==null?b:"."+h;e(this,"deltaMsgObj",S),e(this,"initialDeltaValue",b),e(this,"finalDeltaValue",d),e(this,"deltaBehavior","decrease"),e(this,"realDelta",b-d),this.assert(d-b<0,"expected "+S+" to decrease","expected "+S+" to not decrease")}t.addMethod("decrease",Ce),t.addMethod("decreases",Ce);function $e(a,h){h&&e(this,"message",h);var p=e(this,"deltaMsgObj"),y=e(this,"initialDeltaValue"),m=e(this,"finalDeltaValue"),w=e(this,"deltaBehavior"),b=e(this,"realDelta"),d;w==="change"?d=Math.abs(m-y)===Math.abs(a):d=b===Math.abs(a),this.assert(d,"expected "+p+" to "+w+" by "+a,"expected "+p+" to not "+w+" by "+a)}t.addMethod("by",$e),t.addProperty("extensible",function(){var a=e(this,"object"),h=a===Object(a)&&Object.isExtensible(a);this.assert(h,"expected #{this} to be extensible","expected #{this} to not be extensible")}),t.addProperty("sealed",function(){var a=e(this,"object"),h=a===Object(a)?Object.isSealed(a):!0;this.assert(h,"expected #{this} to be sealed","expected #{this} to not be sealed")}),t.addProperty("frozen",function(){var a=e(this,"object"),h=a===Object(a)?Object.isFrozen(a):!0;this.assert(h,"expected #{this} to be frozen","expected #{this} to not be frozen")}),t.addProperty("finite",function(a){var h=e(this,"object");this.assert(typeof h=="number"&&isFinite(h),"expected #{this} to be a finite number","expected #{this} to not be a finite number")})}});var Zn=O((Ii,Jn)=>{Jn.exports=function(s,o){s.expect=function(t,f){return new s.Assertion(t,f)},s.expect.fail=function(t,f,e,n){throw arguments.length<2&&(e=t,t=void 0),e=e||"expect.fail()",new s.AssertionError(e,{actual:t,expected:f,operator:n},s.expect.fail)}}});var Yn=O((ki,Qn)=>{Qn.exports=function(s,o){var t=s.Assertion;function f(){function e(){return this instanceof String||this instanceof Number||this instanceof Boolean||typeof Symbol=="function"&&this instanceof Symbol||typeof BigInt=="function"&&this instanceof BigInt?new t(this.valueOf(),null,e):new t(this,null,e)}function n(i){Object.defineProperty(this,"should",{value:i,enumerable:!0,configurable:!0,writable:!0})}Object.defineProperty(Object.prototype,"should",{set:n,get:e,configurable:!0});var r={};return r.fail=function(i,l,v,M){throw arguments.length<2&&(v=i,i=void 0),v=v||"should.fail()",new s.AssertionError(v,{actual:i,expected:l,operator:M},r.fail)},r.equal=function(i,l,v){new t(i,v).to.equal(l)},r.Throw=function(i,l,v,M){new t(i,M).to.Throw(l,v)},r.exist=function(i,l){new t(i,l).to.exist},r.not={},r.not.equal=function(i,l,v){new t(i,v).to.not.equal(l)},r.not.Throw=function(i,l,v,M){new t(i,M).to.not.Throw(l,v)},r.not.exist=function(i,l){new t(i,l).to.not.exist},r.throw=r.Throw,r.not.throw=r.not.Throw,r}s.should=f,s.Should=f}});var Hn=O((Ci,Xn)=>{Xn.exports=function(s,o){var t=s.Assertion,f=o.flag;var e=s.assert=function(n,r){var i=new t(null,null,s.assert,!0);i.assert(n,r,"[ negation message unavailable ]")};e.fail=function(n,r,i,l){throw arguments.length<2&&(i=n,n=void 0),i=i||"assert.fail()",new s.AssertionError(i,{actual:n,expected:r,operator:l},e.fail)},e.isOk=function(n,r){new t(n,r,e.isOk,!0).is.ok},e.isNotOk=function(n,r){new t(n,r,e.isNotOk,!0).is.not.ok},e.equal=function(n,r,i){var l=new t(n,i,e.equal,!0);l.assert(r==f(l,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",r,n,!0)},e.notEqual=function(n,r,i){var l=new t(n,i,e.notEqual,!0);l.assert(r!=f(l,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",r,n,!0)},e.strictEqual=function(n,r,i){new t(n,i,e.strictEqual,!0).to.equal(r)},e.notStrictEqual=function(n,r,i){new t(n,i,e.notStrictEqual,!0).to.not.equal(r)},e.deepEqual=e.deepStrictEqual=function(n,r,i){new t(n,i,e.deepEqual,!0).to.eql(r)},e.notDeepEqual=function(n,r,i){new t(n,i,e.notDeepEqual,!0).to.not.eql(r)},e.isAbove=function(n,r,i){new t(n,i,e.isAbove,!0).to.be.above(r)},e.isAtLeast=function(n,r,i){new t(n,i,e.isAtLeast,!0).to.be.least(r)},e.isBelow=function(n,r,i){new t(n,i,e.isBelow,!0).to.be.below(r)},e.isAtMost=function(n,r,i){new t(n,i,e.isAtMost,!0).to.be.most(r)},e.isTrue=function(n,r){new t(n,r,e.isTrue,!0).is.true},e.isNotTrue=function(n,r){new t(n,r,e.isNotTrue,!0).to.not.equal(!0)},e.isFalse=function(n,r){new t(n,r,e.isFalse,!0).is.false},e.isNotFalse=function(n,r){new t(n,r,e.isNotFalse,!0).to.not.equal(!1)},e.isNull=function(n,r){new t(n,r,e.isNull,!0).to.equal(null)},e.isNotNull=function(n,r){new t(n,r,e.isNotNull,!0).to.not.equal(null)},e.isNaN=function(n,r){new t(n,r,e.isNaN,!0).to.be.NaN},e.isNotNaN=function(n,r){new t(n,r,e.isNotNaN,!0).not.to.be.NaN},e.exists=function(n,r){new t(n,r,e.exists,!0).to.exist},e.notExists=function(n,r){new t(n,r,e.notExists,!0).to.not.exist},e.isUndefined=function(n,r){new t(n,r,e.isUndefined,!0).to.equal(void 0)},e.isDefined=function(n,r){new t(n,r,e.isDefined,!0).to.not.equal(void 0)},e.isFunction=function(n,r){new t(n,r,e.isFunction,!0).to.be.a("function")},e.isNotFunction=function(n,r){new t(n,r,e.isNotFunction,!0).to.not.be.a("function")},e.isObject=function(n,r){new t(n,r,e.isObject,!0).to.be.a("object")},e.isNotObject=function(n,r){new t(n,r,e.isNotObject,!0).to.not.be.a("object")},e.isArray=function(n,r){new t(n,r,e.isArray,!0).to.be.an("array")},e.isNotArray=function(n,r){new t(n,r,e.isNotArray,!0).to.not.be.an("array")},e.isString=function(n,r){new t(n,r,e.isString,!0).to.be.a("string")},e.isNotString=function(n,r){new t(n,r,e.isNotString,!0).to.not.be.a("string")},e.isNumber=function(n,r){new t(n,r,e.isNumber,!0).to.be.a("number")},e.isNotNumber=function(n,r){new t(n,r,e.isNotNumber,!0).to.not.be.a("number")},e.isFinite=function(n,r){new t(n,r,e.isFinite,!0).to.be.finite},e.isBoolean=function(n,r){new t(n,r,e.isBoolean,!0).to.be.a("boolean")},e.isNotBoolean=function(n,r){new t(n,r,e.isNotBoolean,!0).to.not.be.a("boolean")},e.typeOf=function(n,r,i){new t(n,i,e.typeOf,!0).to.be.a(r)},e.notTypeOf=function(n,r,i){new t(n,i,e.notTypeOf,!0).to.not.be.a(r)},e.instanceOf=function(n,r,i){new t(n,i,e.instanceOf,!0).to.be.instanceOf(r)},e.notInstanceOf=function(n,r,i){new t(n,i,e.notInstanceOf,!0).to.not.be.instanceOf(r)},e.include=function(n,r,i){new t(n,i,e.include,!0).include(r)},e.notInclude=function(n,r,i){new t(n,i,e.notInclude,!0).not.include(r)},e.deepInclude=function(n,r,i){new t(n,i,e.deepInclude,!0).deep.include(r)},e.notDeepInclude=function(n,r,i){new t(n,i,e.notDeepInclude,!0).not.deep.include(r)},e.nestedInclude=function(n,r,i){new t(n,i,e.nestedInclude,!0).nested.include(r)},e.notNestedInclude=function(n,r,i){new t(n,i,e.notNestedInclude,!0).not.nested.include(r)},e.deepNestedInclude=function(n,r,i){new t(n,i,e.deepNestedInclude,!0).deep.nested.include(r)},e.notDeepNestedInclude=function(n,r,i){new t(n,i,e.notDeepNestedInclude,!0).not.deep.nested.include(r)},e.ownInclude=function(n,r,i){new t(n,i,e.ownInclude,!0).own.include(r)},e.notOwnInclude=function(n,r,i){new t(n,i,e.notOwnInclude,!0).not.own.include(r)},e.deepOwnInclude=function(n,r,i){new t(n,i,e.deepOwnInclude,!0).deep.own.include(r)},e.notDeepOwnInclude=function(n,r,i){new t(n,i,e.notDeepOwnInclude,!0).not.deep.own.include(r)},e.match=function(n,r,i){new t(n,i,e.match,!0).to.match(r)},e.notMatch=function(n,r,i){new t(n,i,e.notMatch,!0).to.not.match(r)},e.property=function(n,r,i){new t(n,i,e.property,!0).to.have.property(r)},e.notProperty=function(n,r,i){new t(n,i,e.notProperty,!0).to.not.have.property(r)},e.propertyVal=function(n,r,i,l){new t(n,l,e.propertyVal,!0).to.have.property(r,i)},e.notPropertyVal=function(n,r,i,l){new t(n,l,e.notPropertyVal,!0).to.not.have.property(r,i)},e.deepPropertyVal=function(n,r,i,l){new t(n,l,e.deepPropertyVal,!0).to.have.deep.property(r,i)},e.notDeepPropertyVal=function(n,r,i,l){new t(n,l,e.notDeepPropertyVal,!0).to.not.have.deep.property(r,i)},e.ownProperty=function(n,r,i){new t(n,i,e.ownProperty,!0).to.have.own.property(r)},e.notOwnProperty=function(n,r,i){new t(n,i,e.notOwnProperty,!0).to.not.have.own.property(r)},e.ownPropertyVal=function(n,r,i,l){new t(n,l,e.ownPropertyVal,!0).to.have.own.property(r,i)},e.notOwnPropertyVal=function(n,r,i,l){new t(n,l,e.notOwnPropertyVal,!0).to.not.have.own.property(r,i)},e.deepOwnPropertyVal=function(n,r,i,l){new t(n,l,e.deepOwnPropertyVal,!0).to.have.deep.own.property(r,i)},e.notDeepOwnPropertyVal=function(n,r,i,l){new t(n,l,e.notDeepOwnPropertyVal,!0).to.not.have.deep.own.property(r,i)},e.nestedProperty=function(n,r,i){new t(n,i,e.nestedProperty,!0).to.have.nested.property(r)},e.notNestedProperty=function(n,r,i){new t(n,i,e.notNestedProperty,!0).to.not.have.nested.property(r)},e.nestedPropertyVal=function(n,r,i,l){new t(n,l,e.nestedPropertyVal,!0).to.have.nested.property(r,i)},e.notNestedPropertyVal=function(n,r,i,l){new t(n,l,e.notNestedPropertyVal,!0).to.not.have.nested.property(r,i)},e.deepNestedPropertyVal=function(n,r,i,l){new t(n,l,e.deepNestedPropertyVal,!0).to.have.deep.nested.property(r,i)},e.notDeepNestedPropertyVal=function(n,r,i,l){new t(n,l,e.notDeepNestedPropertyVal,!0).to.not.have.deep.nested.property(r,i)},e.lengthOf=function(n,r,i){new t(n,i,e.lengthOf,!0).to.have.lengthOf(r)},e.hasAnyKeys=function(n,r,i){new t(n,i,e.hasAnyKeys,!0).to.have.any.keys(r)},e.hasAllKeys=function(n,r,i){new t(n,i,e.hasAllKeys,!0).to.have.all.keys(r)},e.containsAllKeys=function(n,r,i){new t(n,i,e.containsAllKeys,!0).to.contain.all.keys(r)},e.doesNotHaveAnyKeys=function(n,r,i){new t(n,i,e.doesNotHaveAnyKeys,!0).to.not.have.any.keys(r)},e.doesNotHaveAllKeys=function(n,r,i){new t(n,i,e.doesNotHaveAllKeys,!0).to.not.have.all.keys(r)},e.hasAnyDeepKeys=function(n,r,i){new t(n,i,e.hasAnyDeepKeys,!0).to.have.any.deep.keys(r)},e.hasAllDeepKeys=function(n,r,i){new t(n,i,e.hasAllDeepKeys,!0).to.have.all.deep.keys(r)},e.containsAllDeepKeys=function(n,r,i){new t(n,i,e.containsAllDeepKeys,!0).to.contain.all.deep.keys(r)},e.doesNotHaveAnyDeepKeys=function(n,r,i){new t(n,i,e.doesNotHaveAnyDeepKeys,!0).to.not.have.any.deep.keys(r)},e.doesNotHaveAllDeepKeys=function(n,r,i){new t(n,i,e.doesNotHaveAllDeepKeys,!0).to.not.have.all.deep.keys(r)},e.throws=function(n,r,i,l){(typeof r=="string"||r instanceof RegExp)&&(i=r,r=null);var v=new t(n,l,e.throws,!0).to.throw(r,i);return f(v,"object")},e.doesNotThrow=function(n,r,i,l){(typeof r=="string"||r instanceof RegExp)&&(i=r,r=null),new t(n,l,e.doesNotThrow,!0).to.not.throw(r,i)},e.operator=function(n,r,i,l){var v;switch(r){case"==":v=n==i;break;case"===":v=n===i;break;case">":v=n>i;break;case">=":v=n>=i;break;case"<":v=n{var er=[];J.version="4.3.3";J.AssertionError=Qe();var tr=Gn();J.use=function(s){return~er.indexOf(s)||(s(J,tr),er.push(s)),J};J.util=tr;var ko=ce();J.config=ko;var Co=Wn();J.use(Co);var zo=_n();J.use(zo);var Bo=Zn();J.use(Bo);var Fo=Yn();J.use(Fo);var Vo=Hn();J.use(Vo)});var rr=O((Bi,nr)=>{nr.exports=oe()});var Qo={};jr(Qo,{default:()=>Jo,describe:()=>dr,expect:()=>Zo});module.exports=Tr(Qo);var X=qr(rr(),1),Fi=X.default.expect,Vi=X.default.version,Ri=X.default.Assertion,Li=X.default.AssertionError,Ki=X.default.util,Gi=X.default.config,Ui=X.default.use,Wi=X.default.should,$i=X.default.assert,_i=X.default.core,ne=X.default;var sr=require("k6");var ie=(s="")=>new RegExp(`#{${s}}`,"g");var or=s=>typeof s=="function";var ir=(s="",o=250)=>s.length>o?`${s.substring(0,o)}...`:s;var ft=s=>C.util.objDisplay(s),je=s=>ir(s,C.config.truncateVariableThreshold);function Ro(s,o){let[t,f,e,n]=o,r=C.util.flag(s,"negate"),i=C.util.flag(s,"anonymizeMsgFunction"),l=r?e:f;return l=l.replace("but ",""),i&&(l=i(l)),or(l)&&(l=l()),l=l||"",l=l.replace(ie("exp"),()=>je(ft(n))),l}function Lo(s,o="",t){let f=C.util.flag(s,"object"),e=C.util.getActual(s,t),n=C.util.flag(s,"message"),r=o.replace(ie("this"),()=>je(ft(f))).replace(ie("act"),()=>je(ft(e)));return n&&!C.config.aggregateChecks&&(r=n?n+": "+r:r),r}function Ko(s,o=""){let t=C.util.flag(s,"message"),f=o.replace(ie("this"),()=>t||"${this}").replace(ie("act"),()=>"${actual}");return je(f)}function ar(){return function(s,o,t,f,e,n){n=!f&&!e;let r=this,i=[s,o,t,f,e,n],l=C.util.test(r,i),v=C.util.getActual(r,i),M=Ro(r,i),K=Lo(r,M,i),H=C.config.aggregateChecks?Ko(r,M):K;if((0,sr.check)(null,{[H]:()=>l}),!l){let V=je(K),B=C.util.getOperator(r,i),ee={actual:v,expected:f,showDiff:n,operator:B};throw C.config.logFailures&&console.warn(V),new C.AssertionError(V,ee,C.config.includeStack?C.assert:C.util.flag(r,"ssfi"))}}}var Go=(s="")=>s.replace(ie("this"),()=>"");function ur(s=Go){C.util.flag(this,"anonymizeMsgFunction",s)}function cr(){let s=C.util.flag(this,"object"),o=!0;try{s.json("__unlikelyidefintifier1")}catch{o=!1}C.assert(o,"has valid json body","does not have a valid json body",null,null)}ne.config.truncateVariableThreshold=100;ne.config.truncateMsgThreshold=300;ne.config.aggregateChecks=!0;ne.config.logFailures=!1;ne.Assertion.addMethod("anonymize",ur);ne.Assertion.addMethod("validJsonBody",cr);ne.Assertion.overwriteMethod("assert",ar);var C=ne;var hr=require("k6");var fr=require("k6"),lr=(s,o)=>{console.error(`Exception raised in test "${o}". Failing the test and continuing. +${s}`),(0,fr.check)(null,{[`Exception raised "${s}"`]:()=>!1})};function dr(s,o){let t=!0;return(0,hr.group)(s,()=>{try{o(),t=!0}catch(f){f.name!=="AssertionError"&&lr(f,s),t=!1}}),t}var Jo=C,Zo=C.expect; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/common.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/common.ts index 59c33eb16c..fd68c50b70 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/common.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/common.ts @@ -1,23 +1,23 @@ -import { group } from "k6"; +import { describe } from "../k6chaijs.js"; import { Holder, Issuer } from "../actors"; export const issuer = new Issuer(); export const holder = new Holder(); export function connectionFlow() { - group('Issuer initiates connection with Holder', function () { + describe('Issuer initiates connection with Holder', function () { issuer.createHolderConnection(); - }); + }) && - group('Holder accepts connection with Issuer', function () { + describe('Holder accepts connection with Issuer', function () { holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); - }); + }) && - group('Issuer finalizes connection with Holder', function () { + describe('Issuer finalizes connection with Holder', function () { issuer.finalizeConnectionWithHolder(); - }); + }) && - group('Holder finalizes connection with Issuer', function () { + describe('Holder finalizes connection with Issuer', function () { holder.finalizeConnectionWithIssuer(); }); } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts index ac6ae5727d..b09549572e 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts @@ -1,13 +1,13 @@ -import { group } from 'k6'; import { Options } from 'k6/options'; import { Issuer, Holder } from '../../actors'; import { Connection, CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; import { defaultOptions } from "../../scenarios/default"; import merge from "ts-deepmerge"; +import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - 'group_duration{group:::Issuer creates credential offer}': ['avg < 30000'] + 'group_duration{group:::Issuer creates credential offer}': ['avg < 5000'] } } export let options: Options = merge(localOptions, defaultOptions) @@ -15,23 +15,23 @@ export const issuer = new Issuer(); export const holder = new Holder(); export function setup() { - group('Issuer publishes DID', function () { + describe('Issuer publishes DID', function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - group('Holder creates unpublished DID', function () { + describe('Holder creates unpublished DID', function () { holder.createUnpublishedDid(); }); - group('Issuer connects with Holder', function () { + describe('Issuer connects with Holder', function () { issuer.createHolderConnection(); holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); issuer.finalizeConnectionWithHolder(); holder.finalizeConnectionWithIssuer(); }); - group("Issuer creates credential schema", function () { + describe("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); @@ -53,8 +53,7 @@ export default (data: { issuerDid: string; holderDid: string; issuerSchema: Cred issuer.connectionWithHolder = data.connectionWithHolder; holder.connectionWithIssuer = data.connectionWithIssuer; - - group('Issuer creates credential offer', function () { + describe('Issuer creates credential offer', function () { issuer.createCredentialOffer(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts index 1b4b2cda16..7ef8e76829 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts @@ -1,19 +1,19 @@ -import { group } from "k6"; import { Options } from "k6/options"; import { Issuer } from "../../actors"; import { defaultOptions } from "../../scenarios/default"; import merge from "ts-deepmerge"; +import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - 'group_duration{group:::Issuer creates credential schema}': ['avg < 30000'] + 'group_duration{group:::Issuer creates credential schema}': ['avg < 5000'] } } export let options: Options = merge(localOptions, defaultOptions) export const issuer = new Issuer(); export function setup() { - group("Issuer publishes DID", function () { + describe("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); @@ -27,7 +27,7 @@ export default (data: { issuerDid: string }) => { // This is the only way to pass data from setup to default issuer.did = data.issuerDid; - group("Issuer creates credential schema", function () { + describe("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts index 00f9c7d9ba..78598c618c 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts @@ -1,12 +1,12 @@ import { Options } from 'k6/options'; import { Issuer } from '../../actors'; import { defaultOptions } from "../../scenarios/default"; -import { group } from "k6"; import merge from "ts-deepmerge"; +import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - 'group_duration{group:::Issuer create unpublished DID}': ['avg < 30000'] + 'group_duration{group:::Issuer create unpublished DID}': ['avg < 5000'] } } export let options: Options = merge(localOptions, defaultOptions) @@ -14,7 +14,7 @@ export let options: Options = merge(localOptions, defaultOptions) const issuer = new Issuer(); export default () => { - group("Issuer create unpublished DID", function () { + describe("Issuer create unpublished DID", function () { issuer.createUnpublishedDid(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts index 73ce438618..9f590aabb7 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts @@ -1,12 +1,12 @@ import { Options } from "k6/options"; import { Issuer } from "../../actors"; import merge from "ts-deepmerge"; -import { group } from "k6"; import { defaultOptions } from "../../scenarios/default"; +import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - "group_duration{group:::Issuer create published DID}": ["avg < 30000"], + "group_duration{group:::Issuer create published DID}": ["avg < 5000"], }, }; export let options: Options = merge(localOptions, defaultOptions); @@ -14,7 +14,7 @@ export let options: Options = merge(localOptions, defaultOptions); const issuer = new Issuer(); export default () => { - group("Issuer create published DID", function () { + describe("Issuer create published DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts index 2102eff94c..8b86b10623 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts @@ -2,19 +2,20 @@ import { Options } from "k6/options"; import { connectionFlow } from "../common"; import { defaultOptions } from "../../scenarios/default"; import merge from "ts-deepmerge"; + export const localOptions: Options = { thresholds: { "group_duration{group:::Issuer initiates connection with Holder}": [ - "avg < 15000", + "avg<5000", ], "group_duration{group:::Holder accepts connection with Issuer}": [ - "avg < 15000", + "avg<5000", ], "group_duration{group:::Issuer finalizes connection with Holder}": [ - "avg < 30000", + "avg<5000", ], "group_duration{group:::Holder finalizes connection with Issuer}": [ - "avg < 15000", + "avg<5000", ], }, }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts index 89e144524c..28c59ac5d9 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts @@ -1,21 +1,22 @@ -import { group } from "k6"; import { Options } from "k6/options"; import { issuer, holder } from "../common"; import { CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; import { defaultOptions } from "../../scenarios/default"; import merge from "ts-deepmerge"; +import { describe } from "../../k6chaijs.js"; + export const localOptions: Options = { thresholds: { - "group_duration{group:::Issuer connects with Holder}": ["avg < 30000"], + "group_duration{group:::Issuer connects with Holder}": ["avg < 5000"], "group_duration{group:::Issuer creates credential offer for Holder}": [ - "avg < 60000", + "avg < 5000", ], "group_duration{group:::Issuer issues credential to Holder}": [ - "avg < 60000", + "avg < 5000", ], "group_duration{group:::Holder receives credential from Issuer}": [ - "avg < 30000", + "avg < 5000", ], }, }; @@ -23,16 +24,16 @@ export let options: Options = merge(localOptions, defaultOptions); // This is setup code. It runs once at the beginning of the test, regardless of the number of VUs. export function setup() { - group("Issuer publishes DID", function () { + describe("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - group("Issuer creates credential schema", function () { + describe("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); - group("Holder creates unpublished DID", function () { + describe("Holder creates unpublished DID", function () { holder.createUnpublishedDid(); }); @@ -53,32 +54,32 @@ export default (data: { issuer.schema = data.issuerSchema; holder.did = data.holderDid; - group("Issuer connects with Holder", function () { + describe("Issuer connects with Holder", function () { issuer.createHolderConnection(); holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); issuer.finalizeConnectionWithHolder(); holder.finalizeConnectionWithIssuer(); - }); + }) && - group("Issuer creates credential offer for Holder", function () { + describe("Issuer creates credential offer for Holder", function () { issuer.createCredentialOffer(); issuer.waitForCredentialOfferToBeSent(); - }); + }) && - group( + describe( "Holder achieves and accepts credential offer from Issuer", function () { holder.waitAndAcceptCredentialOffer(issuer.credential!.thid); } - ); + ) && - group("Issuer issues credential to Holder", function () { + describe("Issuer issues credential to Holder", function () { issuer.receiveCredentialRequest(); issuer.issueCredential(); issuer.waitForCredentialToBeSent(); - }); + }) && - group("Holder receives credential from Issuer", function () { + describe("Holder receives credential from Issuer", function () { holder.receiveCredential(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts index 83b5a8860a..8670bdb01d 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts @@ -1,19 +1,19 @@ -import { group } from "k6"; import { Options } from "k6/options"; import { Issuer, Holder, Verifier } from "../../actors"; import { CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; import { defaultOptions } from "../../scenarios/default"; import merge from "ts-deepmerge"; +import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - "group_duration{group:::Holder connects with Issuer}": ["avg < 30000"], + "group_duration{group:::Holder connects with Issuer}": ["avg < 5000"], "group_duration{group:::Issuer creates credential offer for Holder}": [ - "avg < 120000", + "avg < 5000", ], - "group_duration{group:::Holder connects with Verifier}": ["avg < 30000"], + "group_duration{group:::Holder connects with Verifier}": ["avg < 5000"], "group_duration{group:::Verifier requests proof from Holder}": [ - "avg < 30000", + "avg < 5000", ], }, }; @@ -24,16 +24,16 @@ const holder = new Holder(); const verifier = new Verifier(); export function setup() { - group("Issuer publishes DID", function () { + describe("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - group("Issuer creates credential schema", function () { + describe("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); - group("Holder creates unpublished DID", function () { + describe("Holder creates unpublished DID", function () { holder.createUnpublishedDid(); }); @@ -53,14 +53,14 @@ export default (data: { issuer.schema = data.issuerSchema; holder.did = data.holderDid; - group("Holder connects with Issuer", function () { + describe("Holder connects with Issuer", function () { issuer.createHolderConnection(); holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); issuer.finalizeConnectionWithHolder(); holder.finalizeConnectionWithIssuer(); - }); + }) && - group("Issuer creates credential offer for Holder", function () { + describe("Issuer creates credential offer for Holder", function () { issuer.createCredentialOffer(); issuer.waitForCredentialOfferToBeSent(); holder.waitAndAcceptCredentialOffer(issuer.credential!.thid); @@ -68,16 +68,16 @@ export default (data: { issuer.issueCredential(); issuer.waitForCredentialToBeSent(); holder.receiveCredential(); - }); + }) && - group("Holder connects with Verifier", function () { + describe("Holder connects with Verifier", function () { verifier.createHolderConnection(); holder.acceptVerifierConnection(verifier.connectionWithHolder!.invitation); verifier.finalizeConnectionWithHolder(); holder.finalizeConnectionWithVerifier(); - }); + }) && - group("Verifier requests proof from Holder", function () { + describe("Verifier requests proof from Holder", function () { verifier.requestProof(); holder.waitAndAcceptProofRequest(verifier.presentation!.thid); verifier.acknowledgeProof(); From 8bf64d2b9d56c3b10d5100848fa6c23f9bd84e52 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 18 Jan 2024 15:04:08 +0000 Subject: [PATCH 04/26] feat(prism-agent): add integration test for metric endpoint Signed-off-by: David Poltorak --- .../kotlin/features/system/SystemSteps.kt | 21 +++++++++++++++++++ .../features/system/metrics_endpoint.feature | 6 ++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature diff --git a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt b/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt index aaee61d9ec..819c98abf0 100644 --- a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt @@ -28,4 +28,25 @@ class SystemSteps { Ensure.that(healthResponse.version).isNotBlank() ) } + + @When("{actor} Issuer makes a request to the metrics endpoint") + fun actorRequestsMetricEndpoint(actor: Actor) { + actor.attemptsTo( + Get.resource("/_system/metrics") + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + ) + } + + @Then("{actor} Issuer sees that the metrics contain background job stats") + fun actorSeesMetrics(actor: Actor) { + val metricsResponse = SerenityRest.lastResponse().get() + actor.attemptsTo( + Ensure.that(metricsResponse).contains("present_proof_flow_did_com_exchange_job_ms_gauge"), + Ensure.that(metricsResponse).contains("connection_flow_did_com_exchange_job_ms_gauge"), + Ensure.that(metricsResponse).contains("issuance_flow_did_com_exchange_job_ms_gauge") + ) + } + } diff --git a/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature new file mode 100644 index 0000000000..8c002e8d2d --- /dev/null +++ b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature @@ -0,0 +1,6 @@ +@system @smoke +Feature: Agent Health Endpoint + +# Scenario: Background job metrics are produced by the service and available to scrape +# When Issuer makes a request to the metrics endpoint +# Then Issuer sees that the metrics contain background job stats From 3732c1598dc8e4a8a6ac3691c832216ce56aaea7 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 18 Jan 2024 14:54:57 +0000 Subject: [PATCH 05/26] feat(prism-agent): upgrade to ZIO HTTP RC4 to improve request performance Signed-off-by: David Poltorak --- SECURITY.md | 17 + build.sbt | 14 +- .../io/iohk/atala/AgentClientAlice.scala | 24 - .../scala/io/iohk/atala/AgentClientBob.scala | 17 - .../AgentClientCoordinateMediation.scala | 34 -- .../src/main/scala/io/iohk/atala/QRcode.scala | 40 -- .../io/iohk/atala/mercury/AgentCli.scala | 487 ------------------ .../io/iohk/atala/mercury/AgentHardCode.scala | 54 -- .../io/iohk/atala/mercury/ZioHttpClient.scala | 52 -- .../service/HttpURIDereferencerImpl.scala | 33 +- .../agent/notification/WebhookPublisher.scala | 34 +- .../agent/server/DidCommHttpServer.scala | 55 +- .../io/iohk/atala/agent/server/Main.scala | 22 +- .../agent/server/http/ZioHttpClient.scala | 86 ++-- .../authentication/oidc/KeycloakClient.scala | 65 +-- 15 files changed, 194 insertions(+), 840 deletions(-) create mode 100644 SECURITY.md delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientAlice.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientBob.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientCoordinateMediation.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/QRcode.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentHardCode.scala delete mode 100644 mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/ZioHttpClient.scala diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..bd1e21eff2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security + +# Reporting Security Issues + +The Open Enterprise Agent (OEA) team and community take security bugs in the components of the OEA ecosystem seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/hyperledger-labs/open-enterprise-agent/security/advisories/new) tab. + +The OEA team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. + +Report security bugs in third-party modules to the person or team maintaining the module. + +## Security Notification Process + +Security notifications will be sent through Discord + +# Security Policy diff --git a/build.sbt b/build.sbt index 331fef7031..2da827f692 100644 --- a/build.sbt +++ b/build.sbt @@ -51,13 +51,13 @@ lazy val V = new { val zioConfig = "3.0.7" val zioLogging = "2.0.1" val zioJson = "0.3.0" - val zioHttp = "3.0.0-RC2" + val zioHttp = "3.0.0-RC4" val zioCatsInterop = "3.3.0" val zioMetricsConnector = "2.1.0" val zioMock = "1.0.0-RC11" val mockito = "3.2.16.0" - // https://mvnrepository.com/artifact/io.circe/circe-core + // https://mvnrepository.com/artifact/io.circe/circe-core val circe = "0.14.6" val tapir = "1.6.4" @@ -89,6 +89,7 @@ lazy val V = new { val nimbusJwt = "10.0.0" val keycloak = "22.0.4" // scala-steward:off + } /** Dependencies */ @@ -648,14 +649,6 @@ lazy val agentDidcommx = project .settings(libraryDependencies += D.munitZio) .dependsOn(agent) //modelsDidcommx -/** Demos agents and services implementation with didcommx */ -lazy val agentCliDidcommx = project - .in(file("mercury/mercury-library/agent-cli-didcommx")) - .settings(name := "mercury-agent-cli-didcommx") - .settings(libraryDependencies += "com.google.zxing" % "core" % "3.5.0") - .settings(libraryDependencies += D.zioHttp) - .dependsOn(agentDidcommx) - // ///** TODO Demos agents and services implementation with did-scala */ // lazy val agentDidScala = // project @@ -879,7 +872,6 @@ lazy val aggregatedProjects: Seq[ProjectReference] = Seq( resolver, agent, agentDidcommx, - agentCliDidcommx, castorCore, polluxVcJWT, polluxCore, diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientAlice.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientAlice.scala deleted file mode 100644 index 015fb3eea0..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientAlice.scala +++ /dev/null @@ -1,24 +0,0 @@ -// package io.iohk.atala - -// import zio._ - -// import io.iohk.atala.mercury._ -// import io.iohk.atala.mercury.given -// import io.circe.Printer -// import io.circe.syntax._ -// import io.circe.Json._ -// import io.circe.parser._ -// import io.circe.JsonObject -// import io.circe.Encoder._ -// import io.iohk.atala.mercury.model.Message -// import io.circe.generic.auto._, io.circe.syntax._ -// import io.circe._, io.circe.parser._ - -// @main def AgentClientAlice() = { - -// val app = AgentPrograms.pickupMessageProgram -// .provide(AgentService.alice, ZioHttpClient.layer) - -// Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(app).getOrThrowFiberFailure() } - -// } diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientBob.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientBob.scala deleted file mode 100644 index a01e929e3c..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientBob.scala +++ /dev/null @@ -1,17 +0,0 @@ -// package io.iohk.atala - -// import zio._ -// import zio.http.service._ -// import io.iohk.atala.mercury._ -// import io.iohk.atala.mercury.model.UnpackMessage -// import io.iohk.atala.mercury.protocol.mailbox.Mailbox.ReadMessage -// import org.didcommx.didcomm.message.Attachment.Data.Json - -// @main def AgentClientBob() = { - -// val app = AgentPrograms.senderProgram -// .provide(AgentService.bob, ZioHttpClient.layer) - -// Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(app).getOrThrowFiberFailure() } - -// } diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientCoordinateMediation.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientCoordinateMediation.scala deleted file mode 100644 index 47556d1381..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/AgentClientCoordinateMediation.scala +++ /dev/null @@ -1,34 +0,0 @@ -package io.iohk.atala - -import zio._ -import io.iohk.atala.mercury._ - -@main def AgentClientGetInvitation() = { - val app = - InvitationPrograms - .getInvitationProgram("http://localhost:8000/oob_url") - .provide(ZioHttpClient.layer) - - Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(app).getOrThrowFiberFailure() } - -} - -// val env = zio.http.Client.default ++ zio.Scope.default - -// @main def AgentClientCoordinateMediationWithRootsId() = { -// val env = ChannelFactory.auto ++ EventLoopGroup.auto() -// val mediatorURL = "http://localhost:8000" -// val app = CoordinateMediationPrograms -// .senderMediationRequestProgram(mediatorURL) -// .provide(AgentService.charlie, HttpClientZhttp.layer) -// Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(app).getOrThrowFiberFailure() } -// } - -// @main def AgentClientCoordinateMediation() = { -// val env = ChannelFactory.auto ++ EventLoopGroup.auto() -// val mediatorURL = "http://localhost:8080" -// val app = CoordinateMediationPrograms -// .senderMediationRequestProgram(mediatorURL) -// .provide(AgentService.charlie, HttpClientZhttp.layer) -// Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(app).getOrThrowFiberFailure() } -// } diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/QRcode.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/QRcode.scala deleted file mode 100644 index a0d925f580..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/QRcode.scala +++ /dev/null @@ -1,40 +0,0 @@ -package io.iohk.atala - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.MultiFormatWriter; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import java.util.Hashtable - -object QRcode { - - def getQr(text: String) = { - val width = 40 - val height = 40 - val qrParam = new Hashtable[EncodeHintType, Object]() - qrParam.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L) - qrParam.put(EncodeHintType.CHARACTER_SET, "utf-8") - try { - val bitMatrix: BitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, qrParam); - toAscii(bitMatrix) - } catch case e: WriterException => e.printStackTrace() - } - - def toAscii(bitMatrix: BitMatrix) = - val sb = new StringBuilder() - for (rows <- 0 to bitMatrix.getHeight - 1) - for (cols <- 0 to bitMatrix.getWidth - 1) - if (!bitMatrix.get(rows, cols)) - sb.append(" ") // white - else sb.append("██") // black - sb.append("\n") - sb.toString() - -} - -@main def QRcodeMain() = { - val text = "https://localhost:8000/?_oob=asfyukfuhgkflajfl" - System.out.println(QRcode.getQr(text)) -} diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala deleted file mode 100644 index 356f13d0d2..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala +++ /dev/null @@ -1,487 +0,0 @@ -package io.iohk.atala.mercury - -import scala.jdk.CollectionConverters.* -import zio.* -import zio.http.{Header as _, *} -import java.io.IOException -import io.iohk.atala.QRcode -import io.iohk.atala.mercury._ -import io.iohk.atala.mercury.model.{given, _} -import io.iohk.atala.mercury.model.error._ -import io.iohk.atala.mercury.protocol.outofbandlogin._ -import io.iohk.atala.mercury.protocol.issuecredential._ -import io.iohk.atala.mercury.protocol.presentproof._ -import io.iohk.atala.mercury.protocol.connection.* -import io.iohk.atala.mercury.protocol.reportproblem.v2._ -import io.iohk.atala.mercury.protocol.invitation.v2.Invitation -import io.iohk.atala.resolvers._ -import scala.language.implicitConversions - -/** AgentCli - * {{{ - * gentDidcommx/runMain io.iohk.atala.mercury.AgentCli - * }}} - */ -object AgentCli extends ZIOAppDefault { - - def questionYN(q: String): ZIO[Any, IOException, Boolean] = { - for { - _ <- Console.printLine(q + " [y(default)/n] ") - ret <- Console.readLine.flatMap { - case "y" | "Y" => ZIO.succeed(true) - case "n" | "N" => ZIO.succeed(false) - case "" => ZIO.succeed(true) // default - case _ => Console.print("[RETRY] ") *> questionYN(q) - } - } yield (ret) - } - - def options(p: Seq[(String, ZIO[Any, MercuryThrowable, Unit])]): ZIO[Any, MercuryThrowable, Unit] = { - for { - _ <- Console.printLine("\n--- Choose an option: ---") - _ <- ZIO.foreach(p.zipWithIndex)(e => Console.printLine(s"${e._2} - ${e._1._1}")) - _ <- Console.readLine.flatMap { e => p.map(_._2).toSeq(e.toInt) } - } yield () - } - - val startLogo = Console.printLine(""" - | ███╗ ███╗███████╗██████╗ ██████╗██╗ ██╗██████╗ ██╗ ██╗ ██████╗██╗ ██╗ - | ████╗ ████║██╔════╝██╔══██╗██╔════╝██║ ██║██╔══██╗╚██╗ ██╔╝ ██╔════╝██║ ██║ - | ██╔████╔██║█████╗ ██████╔╝██║ ██║ ██║██████╔╝ ╚████╔╝ █████╗██║ ██║ ██║ - | ██║╚██╔╝██║██╔══╝ ██╔══██╗██║ ██║ ██║██╔══██╗ ╚██╔╝ ╚════╝██║ ██║ ██║ - | ██║ ╚═╝ ██║███████╗██║ ██║╚██████╗╚██████╔╝██║ ██║ ██║ ╚██████╗███████╗██║ - | ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝ - |DID Comm V2 Agent - CLI tool for debugging - Build by Atala (IOHK) - |""".stripMargin) - - // val env = zio.http.Client.default ++ zio.Scope.default - - def askForMediation = { - for { - _ <- Console.printLine("Enter the Mediator URL (defualt is 'http://localhost:8000')") - url <- Console.readLine.flatMap { - case "" => ZIO.succeed("http://localhost:8000") // defualt - case str => ZIO.succeed(str) - } - _ <- CoordinateMediationPrograms - .senderMediationRequestProgram(mediatorURL = url) - // .provideSomeLayer(zio.http.Client.default) - // .provideSomeLayer(zio.Scope.default) - } yield () - } - - // FIXME create a new MODEL for Login protocol - def generateLoginInvitation = { - import io.iohk.atala.mercury.protocol.outofbandlogin._ - - // InvitationPrograms.createInvitationV2().map(oob => Response.text(serverUrl + oob)) - for { - agentService <- ZIO.service[DidAgent] - didCommService <- ZIO.service[DidOps] - invitation = OutOfBandLoginInvitation(from = agentService.id) - invitationSigned <- didCommService.packSigned(invitation.makeMsg) - serverUrl = s"https://didcomm-bootstrap.atalaprism.com?_oob=${invitationSigned.base64}" // FIXME locahost - _ <- Console.printLine(QRcode.getQr(serverUrl).toString) - _ <- Console.printLine(serverUrl) - _ <- Console.printLine(invitation.id + " -> " + invitation) - } yield () - } - - def generateConnectionInvitation = { - import io.iohk.atala.mercury.protocol.invitation._ - for { - agentService <- ZIO.service[DidAgent] - invitation = ConnectionInvitation.makeConnectionInvitation(from = agentService.id) - serverUrl = s"https://didcomm-bootstrap.atalaprism.com?_oob=${invitation.toBase64}" - _ <- Console.printLine(serverUrl) - _ <- Console.printLine(invitation.id + " -> " + invitation) - } yield () - } - - def loginInvitation = { - import io.iohk.atala.mercury.protocol.outofbandlogin._ - - def reaOutOfBandLoginInvitation(msg: org.didcommx.didcomm.message.Message): OutOfBandLoginInvitation = { - // OutOfBandLoginInvitation(`type` = msg.piuri, id = msg.id, from = msg.from.get) - OutOfBandLoginInvitation(`type` = msg.getType(), id = msg.getId(), from = DidId(msg.getFrom())) - } - - for { - _ <- Console.printLine("Read OutOfBand Invitation") - data <- Console.readLine.flatMap { - case "" => ZIO.fail(???) // TODO retry - case url => ZIO.succeed(Utils.parseLink(url).getOrElse(???)) /// TODO make ERROR - } - didCommService <- ZIO.service[DidOps] - msg <- didCommService.unpack(data) - outOfBandLoginInvitation = reaOutOfBandLoginInvitation(msg.message) - agentService <- ZIO.service[DidAgent] - reply = outOfBandLoginInvitation.reply(agentService.id) - _ <- Console.printLine(s"Replying to ${outOfBandLoginInvitation.id} with $reply") - - res <- MessagingService.send(reply.makeMsg) - _ <- Console.printLine(res.bodyAsString) - } yield () - } - - def proposeAndSendCredential: ZIO[DidOps & DidAgent & DIDResolver & HttpClient, MercuryError | IOException, Unit] = { - for { - - _ <- Console.printLine("Propose Credential") - _ <- Console.printLine("What is the the Playload") - playloadData <- Console.readLine.flatMap { - case "" => ZIO.succeed("playload") - case data => ZIO.succeed(data) - } - - attachmentDescriptor = - AttachmentDescriptor.buildJsonAttachment(payload = playloadData) - attribute1 = Attribute(name = "name", value = "Joe Blog") - attribute2 = Attribute(name = "dob", value = "01/10/1947") - credentialPreview = CredentialPreview(attributes = Seq(attribute1, attribute2)) - - agentService <- ZIO.service[DidAgent] - _ <- Console.printLine(s"Send to (ex: ${agentService.id})") - sendTo <- Console.readLine.flatMap { - case "" => ZIO.succeed(agentService.id) - case did => ZIO.succeed(DidId(did)) - } - - proposeCredential = ProposeCredential( - body = ProposeCredential.Body( - goal_code = Some("goal_code"), - comment = None, - credential_preview = Some(credentialPreview), - ), - attachments = Seq(attachmentDescriptor), - from = agentService.id, - to = sendTo, - ) - _ <- Console.printLine(proposeCredential) - msg = proposeCredential.makeMessage - _ <- Console.printLine("Sending: " + msg) - - _ <- MessagingService.send(msg) - } yield () - } - - def presentProof: ZIO[DidOps & DidAgent & DIDResolver & HttpClient, MercuryError | IOException, Unit] = { - for { - _ <- Console.printLine("Present Proof") - agentService <- ZIO.service[DidAgent] - - _ <- Console.printLine(s"Request proof from did (ex: ${agentService.id})") - requestTo <- Console.readLine.flatMap { - case "" => ZIO.succeed(agentService.id) - case did => ZIO.succeed(DidId(did)) - } - - // Make a Request - body = RequestPresentation.Body(goal_code = Some("Presentation Request")) - presentationAttachmentAsJson = """{ - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "us.gov/DriverLicense", - "credential_manifest": {} - }""" - - attachmentDescriptor = AttachmentDescriptor.buildJsonAttachment(payload = presentationAttachmentAsJson) - requestPresentation = RequestPresentation( - body = body, - attachments = Seq(attachmentDescriptor), - to = requestTo, - from = agentService.id, - ) - msg = requestPresentation.makeMessage - _ <- Console.printLine("Sending: " + msg) - _ <- MessagingService.send(msg) - } yield () - } - - def problemReport: ZIO[DidOps & DidAgent & DIDResolver & HttpClient, MercuryError | IOException, Unit] = { - for { - _ <- Console.printLine("Problem Report") - agentService <- ZIO.service[DidAgent] - - _ <- Console.printLine(s"Problem report to did (ex: ${agentService.id})") - requestTo <- Console.readLine.flatMap { - case "" => ZIO.succeed(agentService.id) - case did => ZIO.succeed(DidId(did)) - } - // Make a Problem Report - reportproblem = ReportProblem.build( - fromDID = agentService.id, - toDID = requestTo, - pthid = "1e513ad4-48c9-444e-9e7e-5b8b45c5e325", - code = ProblemCode("e.p.xfer.cant-use-endpoint"), - comment = Some("Unable to use the {1} endpoint for {2}.") - ) - msg = reportproblem.toMessage - _ <- Console.printLine("Sending: " + msg) - _ <- MessagingService.send(msg) - } yield () - } - - def connect: ZIO[DidOps & DidAgent & DIDResolver & HttpClient, MercuryError | IOException, Unit] = { - - import io.iohk.atala.mercury.protocol.invitation.OutOfBand - import io.circe._, io.circe.parser._ - for { - agentService <- ZIO.service[DidAgent] - _ <- Console.printLine("Read OutOfBand Invitation") - data <- Console.readLine.flatMap { - case "" => ZIO.fail(???) // TODO retry - case url => ZIO.succeed(OutOfBand.parseLink(url).getOrElse(???)) /// TODO make ERROR - } - _ <- Console.printLine(s"Decoded Invitation = $data") - parseResult = parse(data).getOrElse(null) - connectionInvitation = parseResult.as[Invitation].getOrElse(???) - _ <- Console.printLine(s"Invitation to ${connectionInvitation.id} with $connectionInvitation") - connectionRequest = ConnectionRequest( - from = agentService.id, - to = connectionInvitation.from, - thid = None, - pthid = Some(connectionInvitation.id), - body = ConnectionRequest.Body(goal_code = Some("connect"), goal = Some("Establish Connection")) - ) - msg = connectionRequest.makeMessage - _ <- Console.printLine("Sending: " + msg) - _ <- MessagingService.send(msg) - } yield () - } - - private def webServer: App[DidOps & DidAgent & DIDResolver & HttpClient] = { - val header = "content-type" -> MediaTypes.contentTypeEncrypted - val (expectedKey, expectedValue) = header - - Http - .collectZIO[Request] { - case req @ Method.POST -> Root - if req.rawHeader(expectedKey).fold(false) { _.equalsIgnoreCase(expectedValue) } => - val res = req.body.asString - .catchNonFatalOrDie(ex => ZIO.fail(ParseResponse(ex))) - .flatMap { data => - webServerProgram(data).catchAll { ex => - ZIO.fail(mercuryErrorAsThrowable(ex)) - } - } - .map(str => Response.text(str)) - - res - case Method.GET -> Root / "test" => ZIO.succeed(Response.text("Test ok!")) - case req => - ZIO.logWarning(s"Received a not DID Comm v2 message: ${req}") *> - ZIO.succeed(Response.text(s"The request must be a POST to root with the Header $header")) - } - .mapError(throwable => Response.fromHttpError(HttpError.InternalServerError(cause = Some(throwable)))) - } - - def startEndpoint: ZIO[DidOps & DidAgent & DIDResolver & HttpClient, IOException, Unit] = for { - _ <- Console.printLine("Setup an endpoint") - agentService <- ZIO.service[DidAgent] - - defaultPort = UniversalDidResolver - .resolve(agentService.id.value) - .get() - .getDidCommServices() - .asScala - .toSeq - .headOption - .map(s => s.getServiceEndpoint()) - .flatMap(e => URL.decode(e).toOption) - .flatMap(_.port) - .getOrElse(8081) // default - - _ <- Console.printLine(s"Insert endpoint port ($defaultPort default) for (http://localhost:port)") - port <- Console.readLine.flatMap { - case "" => ZIO.succeed(defaultPort) - case str => ZIO.succeed(str.toIntOption.getOrElse(defaultPort)) - } - server = { - val config = Server.Config.default.copy(address = new java.net.InetSocketAddress(port)) - ZLayer.succeed(config) >>> Server.live - } - _ <- Server - .serve(webServer) - .provideSomeLayer(server) - .debug - .flatMap(e => Console.printLine("Endpoint stop")) - .catchAll { case ex => Console.printLine(s"Endpoint FAIL ${ex.getMessage()}") } - .fork - _ <- Console.printLine(s"Endpoint Started of port '$port'") - } yield () - - def run = for { - _ <- startLogo - // makeNewDID <- questionYN("Generate new 'peer' DID?") - _ <- Console.printLine("Generating a new 'peer' DID!") - // haveServiceEndpoint <- questionYN("Do you have a serviceEndpoint url? e.g http://localhost:8080/myendpoint") - // ZIO.when(haveServiceEndpoint)( // ) - _ <- Console.printLine("Enter the serviceEndpoint URL (defualt None) or port for http://localhost:port") - serviceEndpoint <- Console.readLine.flatMap { - case "" => ZIO.succeed(None) // default - case str if str.toIntOption.isDefined => ZIO.succeed(str.toIntOption.map(port => s"http://localhost:$port")) - case str => ZIO.succeed(Some(str)) - } - - didPeer <- for { - peer <- ZIO.succeed(PeerDID.makePeerDid(serviceEndpoint = serviceEndpoint)) - // jwkForKeyAgreement <- ZIO.succeed(PeerDID.makeNewJwkKeyX25519) - // jwkForKeyAuthentication <- ZIO.succeed(PeerDID.makeNewJwkKeyEd25519) - // (jwkForKeyAgreement, jwkForKeyAuthentication) - _ <- Console.printLine(s"New DID: ${peer.did}") *> - Console.printLine(s"JWK for KeyAgreement: ${peer.jwkForKeyAgreement.toJSONString}") *> - Console.printLine(s"JWK for KeyAuthentication: ${peer.jwkForKeyAuthentication.toJSONString}") - } yield (peer) - - agentService = AgentPeerService.makeLayer(didPeer) - - layers: ZLayer[Any, Nothing, DidOps & DidAgent & DIDResolver & HttpClient] = - DidCommX.liveLayer ++ agentService ++ DIDResolver.layer ++ ZioHttpClient.layer - - _ <- options( - Seq( - "none" -> ZIO.unit, - "Show DID" -> Console.printLine(didPeer), - "Get DID Document" -> Console.printLine("DID Document:") *> Console.printLine(didPeer.getDIDDocument), - "Start WebServer endpoint" -> startEndpoint.provide(layers), - "Ask for Mediation Coordinate" -> askForMediation.provide(layers), - "Generate login invitation" -> generateLoginInvitation.provide(DidCommX.liveLayer ++ agentService), - "Login with DID" -> loginInvitation.provide(layers), - "Propose Credential" -> proposeAndSendCredential.provide(layers), - "Present Proof" -> presentProof.provide(layers), - "Generate Connection invitation" -> generateConnectionInvitation.provide(DidCommX.liveLayer ++ agentService), - "Connect" -> connect.provide(layers), - "Problem Report" -> problemReport.provide(layers), - ) - ).repeatWhile((_) => true) - - } yield () - - def webServerProgram( - jsonString: String - ): ZIO[DidOps & DidAgent & DIDResolver & HttpClient, MercuryThrowable, String] = { // TODO Throwable - import io.iohk.atala.mercury.DidOps.* - ZIO.logAnnotate("request-id", java.util.UUID.randomUUID.toString()) { - for { - _ <- ZIO.logInfo("Received new message") - _ <- ZIO.logTrace(jsonString) - msg <- unpack(jsonString).map(_.message) - ret <- { - msg.getType match { - case s if s == OutOfBandloginReply.piuri => - for { - _ <- ZIO.logInfo("OutOfBandloginReply: " + msg) - } yield ("OutOfBandloginReply") - - // ######################## - // ### issue-credential ### - // ######################## - case s if s == ProposeCredential.`type` => // Issuer - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Issuer in issue-credential:") - _ <- ZIO.logInfo("Got ProposeCredential: " + msg) - offer = OfferCredential.makeOfferToProposeCredential(msg) // OfferCredential - - didCommService <- ZIO.service[DidOps] - msgToSend = offer.makeMessage - _ <- MessagingService.send(msgToSend) - } yield ("OfferCredential Sent") - - case s if s == OfferCredential.`type` => // Holder - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Holder in issue-credential:") - _ <- ZIO.logInfo("Got OfferCredential: " + msg) - // store on BD TODO //pc = OfferCredential.readFromMessage(msg) - requestCredential = RequestCredential.makeRequestCredentialFromOffer(msg) // RequestCredential - - didCommService <- ZIO.service[DidOps] - msgToSend = requestCredential.makeMessage - _ <- MessagingService.send(msgToSend) - } yield ("RequestCredential Sent") - - case s if s == RequestCredential.`type` => // Issuer - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Issuer in issue-credential:") - _ <- ZIO.logInfo("Got RequestCredential: " + msg) - issueCredential = IssueCredential.makeIssueCredentialFromRequestCredential(msg) // IssueCredential - - didCommService <- ZIO.service[DidOps] - msgToSend = issueCredential.makeMessage - _ <- MessagingService.send(msgToSend) - } yield ("IssueCredential Sent") - - case s if s == IssueCredential.`type` => // Holder - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Holder in issue-credential:") - _ <- ZIO.logInfo("Got IssueCredential: " + msg) - } yield ("IssueCredential Received") - // ###################################################################### - // ######################## - // ### Present-Proof ### - // ######################## - case s if s == RequestPresentation.`type` => // Prover - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Prover in Present-Proof:") - requestPresentation = RequestPresentation.readFromMessage(msg) - _ <- ZIO.logInfo("Got RequestPresentation: " + requestPresentation) - presentation = Presentation.makePresentationFromRequest(msg) - didCommService <- ZIO.service[DidOps] - msgToSend = presentation.makeMessage - _ <- MessagingService.send(msgToSend) - } yield ("Presentation Sent") - case s if s == Presentation.`type` => // Verifier - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As an Verifier in Present-Proof:") - presentation = Presentation.readFromMessage(msg) - _ <- ZIO.logInfo("Got Presentation: " + presentation) - } yield ("Presentation Recived") - // ######################## - // ### ReportProblem ### - // ######################## - case s if s == ReportProblem.`type` => // receiver - for { - _ <- ZIO.logInfo("Received Problem report") - reportProblem = ReportProblem.readFromMessage(msg) - _ <- ZIO.logInfo("Got Problemreport: " + reportProblem) - } yield ("Problemreport Recived") - // ######################## - // ### Comnnect ### - // ######################## - case s if s == ConnectionRequest.`type` => // Inviter - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As Inviter in Connect:") - connectionRequest = ConnectionRequest.fromMessage(msg).toOption.get // TODO .get - _ <- ZIO.logInfo("Got ConnectionRequest: " + connectionRequest) - _ <- ZIO.logInfo("Creating New PeerDID...") - // peer <- ZIO.succeed(PeerDID.makePeerDid(serviceEndpoint = serviceEndpoint)) TODO - // _ <- ZIO.logInfo(s"My new DID => $peer") - connectionResponse = ConnectionResponse.makeResponseFromRequest(msg).toOption.get // TODO .get - msgToSend = connectionResponse.makeMessage - _ <- MessagingService.send(msgToSend) - } yield ("Connection Request Sent") - case s if s == ConnectionResponse.`type` => // Invitee - for { - _ <- ZIO.logInfo("*" * 100) - _ <- ZIO.logInfo("As Invitee in Connect:") - connectionResponse = ConnectionResponse.fromMessage(msg).toOption.get // TODO .get - _ <- ZIO.logInfo("Got Connection Response: " + connectionResponse) - } yield ("Connection established") - - case "https://didcomm.org/routing/2.0/forward" => ??? // SEE mediator - case "https://atalaprism.io/mercury/mailbox/1.0/ReadMessages" => ??? // SEE mediator - case "https://didcomm.org/coordinate-mediation/2.0/mediate-request" => ??? // SEE mediator - case _ => ZIO.succeed("Unknown Message Type") - } - } - } yield (ret) - } - } - -} diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentHardCode.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentHardCode.scala deleted file mode 100644 index 1547dcd6dc..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentHardCode.scala +++ /dev/null @@ -1,54 +0,0 @@ -package io.iohk.atala.mercury - -import zio._ -import io.circe._ -import io.circe.syntax._ - -import io.iohk.atala.mercury.model.{given, _} -import io.iohk.atala.mercury.protocol.issuecredential._ -import java.io.IOException - -import scala.language.implicitConversions - -object AgentHardCode extends ZIOAppDefault { - - def run = for { - didPeer <- for { - peer <- ZIO.succeed(PeerDID.makePeerDid()) // (serviceEndpoint = serviceEndpoint)) - _ <- Console.printLine(s"New DID: ${peer.did}") *> - Console.printLine(s"JWK for KeyAgreement: ${peer.jwkForKeyAgreement.toJSONString}") *> - Console.printLine(s"JWK for KeyAuthentication: ${peer.jwkForKeyAuthentication.toJSONString}") - } yield (peer) - _ <- test.provide(DidCommX.liveLayer, AgentPeerService.makeLayer(didPeer)) - } yield () - - val attribute1 = Attribute(name = "name", value = "Joe Blog") - val attribute2 = Attribute(name = "dob", value = "01/10/1947") - val credentialPreview = CredentialPreview(attributes = Seq(attribute1, attribute2)) - val body = ProposeCredential.Body( - goal_code = Some("Propose Credential"), - credential_preview = Some(credentialPreview), - ) - - def test: ZIO[DidOps & DidAgent, IOException, Unit] = { - for { - agentService <- ZIO.service[DidAgent] - opsService <- ZIO.service[DidOps] - msg = Message( - `type` = "TEST", - from = Some(agentService.id), - to = Seq.empty, - body = body.asJson.asObject.get - ) - // signed <- didCommService.packSigned(msg) - ttt <- opsService.packEncrypted(msg, to = agentService.id) - msg2 <- opsService.unpack(ttt.string) - _ <- Console.printLine(msg) - - aaa = msg: org.didcommx.didcomm.message.Message - _ <- Console.printLine("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") - _ <- Console.printLine(aaa.getAttachments()) - } yield () - } - -} diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/ZioHttpClient.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/ZioHttpClient.scala deleted file mode 100644 index fec9e2b7d2..0000000000 --- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/ZioHttpClient.scala +++ /dev/null @@ -1,52 +0,0 @@ -package io.iohk.atala.mercury - -import zio._ -import zio.http.{Header as _, *} -import io.iohk.atala.mercury._ -object ZioHttpClient { - val layer = ZLayer.succeed(new ZioHttpClient()) -} - -class ZioHttpClient extends HttpClient { - - override def get(url: String): Task[HttpResponse] = - zio.http.Client - .request(url) - .provideSomeLayer(zio.http.Client.default) - .provideSomeLayer(zio.Scope.default) - .flatMap { response => - response.headers.toSeq.map(e => e) - response.body.asString - .map(body => - HttpResponse( - response.status.code, - response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, - body - ) - ) - } - - def postDIDComm(url: String, data: String): Task[HttpResponse] = - zio.http.Client - .request( - url = url, // TODO make ERROR type - method = Method.POST, - headers = Headers("content-type" -> "application/didcomm-encrypted+json"), - // headers = Headers("content-type" -> MediaTypes.contentTypeEncrypted), - content = Body.fromChunk(Chunk.fromArray(data.getBytes)), - // ssl = ClientSSLOptions.DefaultSSL, - ) - .provideSomeLayer(zio.http.Client.default) - .provideSomeLayer(zio.Scope.default) - .flatMap { response => - response.headers.toSeq.map(e => e) - response.body.asString - .map(body => - HttpResponse( - response.status.code, - response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, - body - ) - ) - } -} diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala index 5cc4060f1d..4dc1ac0097 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala @@ -1,31 +1,38 @@ package io.iohk.atala.pollux.core.service import io.iohk.atala.pollux.core.service.URIDereferencerError.{ConnectionError, ResourceNotFound, UnexpectedError} -import zio.http.* import zio.* +import zio.http.* +import zio.stream.ZSink import java.net.URI +import java.nio.charset.StandardCharsets class HttpURIDereferencerImpl(client: Client) extends URIDereferencer { override def dereference(uri: URI): IO[URIDereferencerError, String] = { - val result: ZIO[Client, URIDereferencerError, String] = for { - response <- Client.request(uri.toString).mapError(t => ConnectionError(t.getMessage)) + for { + url <- ZIO.fromOption(URL.fromURI(uri)).mapError(_ => ConnectionError(s"Invalid URI: $uri")) + response <- client + .request(Request(url = url)) + .mapError(t => ConnectionError(t.getMessage)) + .provideSomeLayer(zio.Scope.default) body <- response.status match { case Status.Ok => response.body.asString.mapError(t => UnexpectedError(t.getMessage)) - case Status.NotFound if !response.status.isError => ZIO.fail(ResourceNotFound(uri)) - case _ if response.status.isError => - val err = response match { - case Response.GetError(error) => Some(error) - case _ => None - } - ZIO.fail(UnexpectedError(s"HTTP response error: $err")) - case _ => - ZIO.fail(UnexpectedError("Unknown error")) + case Status.NotFound => + ZIO.fail(ResourceNotFound(uri)) + case status if status.isError => + response.body.asStream + .take(1024) // Only take the first 1024 bytes from the response body (if any). + .runFold(Array.empty[Byte])(_ :+ _) + .map(new String(_, StandardCharsets.UTF_8)) + .orDie + .flatMap(errorMessage => ZIO.fail(UnexpectedError(s"HTTP response error: $status - $errorMessage"))) + case status => + ZIO.fail(UnexpectedError(s"Unexpected response status: $status")) } } yield body - result.provide(ZLayer.succeed(client)) } } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala index c3d79d94a6..e075927ce1 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala @@ -18,7 +18,8 @@ import zio.json.* class WebhookPublisher( appConfig: AppConfig, notificationService: EventNotificationService, - walletService: WalletManagementService + walletService: WalletManagementService, + client: Client ) { private val config = appConfig.agent.webhookPublisher @@ -93,37 +94,40 @@ class WebhookPublisher( private[this] def notifyWebhook[A](event: Event[A], url: String, headers: Headers)(implicit encoder: JsonEncoder[A] ): ZIO[Client, UnexpectedError, Unit] = { - for { + val result = for { _ <- ZIO.logDebug(s"Sending event: $event to HTTP webhook URL: $url.") + url <- ZIO.fromEither(URL.decode(url)).orDie response <- Client .request( - url = url, - method = Method.POST, - headers = baseHeaders ++ headers, - content = Body.fromString(event.toJson) + Request( + url = url, + method = Method.POST, + headers = baseHeaders ++ headers, + body = Body.fromString(event.toJson) + ) ) .timeoutFail(new RuntimeException("Client request timed out"))(5.seconds) .mapError(t => UnexpectedError(s"Webhook request error: $t")) - resp <- if response.status.isSuccess then ZIO.unit else { - val err = response match { - case Response.GetError(error) => Some(error) - case _ => None - } +// val err = response match { +// case Response.GetError(error) => Some(error) +// case _ => None +// } ZIO.fail( UnexpectedError( - s"Unsuccessful webhook response: [status: ${response.status} [error: ${err.getOrElse("none")}]" + s"Failed" +// s"Unsuccessful webhook response: [status: ${response.status} [error: ${err.getOrElse("none")}]" ) ) - } } yield resp + result.provide(ZLayer.succeed(client) ++ Scope.default) } } object WebhookPublisher { - val layer: URLayer[AppConfig & EventNotificationService & WalletManagementService, WebhookPublisher] = - ZLayer.fromFunction(WebhookPublisher(_, _, _)) + val layer: URLayer[AppConfig & EventNotificationService & WalletManagementService & Client, WebhookPublisher] = + ZLayer.fromFunction(WebhookPublisher(_, _, _, _)) } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala index a4c3b2c620..6ed9677958 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala @@ -48,29 +48,50 @@ object DidCommHttpServer { private def didCommServiceEndpoint: HttpApp[ DidOps & CredentialService & PresentationService & ConnectionService & ManagedDIDService & HttpClient & - DIDResolver & DIDNonSecretStorage & AppConfig, - Nothing - ] = Http.collectZIO[Request] { - case req @ Method.POST -> Root - if req.rawHeader("content-type").fold(false) { _.equalsIgnoreCase(MediaTypes.contentTypeEncrypted) } => - val result = for { - data <- req.body.asString.mapError(e => RequestBodyParsingError(e.getMessage)) - _ <- webServerProgram(data) - } yield Response.ok - + DIDResolver & DIDNonSecretStorage & AppConfig + ] = + val rootRoute = Method.POST / "" -> handler { (req: Request) => + val result = req.body.asString + .mapError(e => RequestBodyParsingError(e.getMessage)) + .flatMap { data => + webServerProgram(data) + } + .map(_ => Response.ok) result .tapError(error => ZIO.logErrorCause("Error processing incoming DIDComm message", Cause.fail(error))) .catchAll { - case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) + case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) case _: DIDCommMessageParsingError => ZIO.succeed(Response.status(Status.BadRequest)) - case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) - case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) + case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) } + } - } +// HttpApp.collectZIO[Request] { +// case req @ Method.POST -> Root +// if req.rawHeader("content-type").fold(false) { _.equalsIgnoreCase(MediaTypes.contentTypeEncrypted) } => +// val result = for { +// data <- req.body.asString.mapError(e => RequestBodyParsingError(e.getMessage)) +// _ <- webServerProgram(data) +// } yield Response.ok +// +// result +// .tapError(error => ZIO.logErrorCause("Error processing incoming DIDComm message", Cause.fail(error))) +// .catchAll { +// case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) +// case _: DIDCommMessageParsingError => ZIO.succeed(Response.status(Status.BadRequest)) +// case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) +// case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) +// case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) +// case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) +// case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) +// } + + Routes(rootRoute).toHttpApp + private[this] def extractFirstRecipientDid(jsonMessage: String): IO[ParsingFailure | DecodingFailure, String] = { val doc = parse(jsonMessage).getOrElse(Json.Null) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala index aebb6e4456..7ba050ff96 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala @@ -48,13 +48,11 @@ import io.iohk.atala.resolvers.DIDResolver import io.iohk.atala.system.controller.SystemControllerImpl import io.micrometer.prometheus.{PrometheusConfig, PrometheusMeterRegistry} import zio.* -import zio.http.Client import zio.metrics.connectors.micrometer import zio.metrics.connectors.micrometer.MicrometerConfig import zio.metrics.jvm.DefaultJvmMetrics import java.security.Security -import scala.language.implicitConversions object MainApp extends ZIOAppDefault { @@ -62,7 +60,7 @@ object MainApp extends ZIOAppDefault { // FIXME: remove this when db app user have correct privileges provisioned by k8s operator. // This should be executed before migration to have correct privilege for new objects. - val preMigrations = for { + private val preMigrations = for { _ <- ZIO.logInfo("running pre-migration steps.") appConfig <- ZIO.service[AppConfig].provide(SystemModule.configLayer) _ <- PolluxMigrations @@ -76,7 +74,7 @@ object MainApp extends ZIOAppDefault { .provide(RepoModule.agentTransactorLayer) } yield () - val migrations = for { + private val migrations = for { _ <- ZIO.serviceWithZIO[PolluxMigrations](_.migrate) _ <- ZIO.serviceWithZIO[ConnectMigrations](_.migrate) _ <- ZIO.serviceWithZIO[AgentMigrations](_.migrate) @@ -86,6 +84,20 @@ object MainApp extends ZIOAppDefault { _ <- AgentMigrations.validateRLS.provide(RepoModule.agentContextAwareTransactorLayer) } yield () + private val zioHttpClientLayer = { + import zio.http.netty.NettyConfig + import zio.http.{ConnectionPoolConfig, DnsResolver, ZClient} + (ZLayer.succeed( + ZClient.Config.default.copy( + connectionPool = ConnectionPoolConfig.Disabled, + idleTimeout = Some(2.seconds), + connectionTimeout = Some(2.seconds), + ) + ) ++ + ZLayer.succeed(NettyConfig.default) ++ + DnsResolver.default) >>> ZClient.live + } + override def run: ZIO[Any, Throwable, Unit] = { val app = for { @@ -178,7 +190,7 @@ object MainApp extends ZIOAppDefault { // event notification service ZLayer.succeed(500) >>> EventNotificationServiceImpl.layer, // HTTP client - Client.default, + zioHttpClientLayer, Scope.default, ) } yield app diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala index 060bd131f4..ab41360394 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala @@ -1,53 +1,59 @@ package io.iohk.atala.agent.server.http -import zio._ -import zio.http.{Header => _, *} -import io.iohk.atala.mercury._ +import io.iohk.atala.mercury.* +import zio.* +import zio.http.{Header as _, *} + +import java.time.Instant object ZioHttpClient { - val layer = ZLayer.succeed(new ZioHttpClient()) + val layer: URLayer[Client, ZioHttpClient] = ZLayer.fromFunction(new ZioHttpClient(_)) } -class ZioHttpClient extends HttpClient { +class ZioHttpClient(client: zio.http.Client) extends HttpClient { override def get(url: String): Task[HttpResponse] = - zio.http.Client - .request(url) - .provideSomeLayer(zio.http.Client.default) - .provideSomeLayer(zio.Scope.default) - .flatMap { response => - response.headers.toSeq.map(e => e) - response.body.asString - .map(body => - HttpResponse( - response.status.code, - response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, - body + for { + url <- ZIO.fromEither(URL.decode(url)).orDie + response <- client + .request(Request(url = url)) + .provideSomeLayer(zio.Scope.default) + .flatMap { response => + response.headers.toSeq.map(e => e) + response.body.asString + .map(body => + HttpResponse( + response.status.code, + response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, + body + ) ) - ) - } + } + } yield response def postDIDComm(url: String, data: String): Task[HttpResponse] = - zio.http.Client - .request( - url = url, // TODO make ERROR type - method = Method.POST, - headers = Headers("content-type" -> "application/didcomm-encrypted+json"), - // headers = Headers("content-type" -> MediaTypes.contentTypeEncrypted), - content = Body.fromChunk(Chunk.fromArray(data.getBytes)), - // ssl = ClientSSLOptions.DefaultSSL, - ) - .provideSomeLayer(zio.http.Client.default) - .provideSomeLayer(zio.Scope.default) - .flatMap { response => - response.headers.toSeq.map(e => e) - response.body.asString - .map(body => - HttpResponse( - response.status.code, - response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, - body - ) + for { + url <- ZIO.fromEither(URL.decode(url)).orDie + response <- client + .request( + Request( + url = url, // TODO make ERROR type + method = Method.POST, + headers = Headers("content-type" -> "application/didcomm-encrypted+json"), + body = Body.fromChunk(Chunk.fromArray(data.getBytes)) ) - } + ) + .provideSomeLayer(zio.Scope.default) + .flatMap { response => + response.headers.toSeq.map(e => e) + response.body.asString + .map(body => + HttpResponse( + response.status.code, + response.headers.map(h => Header(h.headerName, h.renderedValue)).toSeq, + body + ) + ) + } + } yield response } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/iam/authentication/oidc/KeycloakClient.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/iam/authentication/oidc/KeycloakClient.scala index 9b5953709b..88260265c7 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/iam/authentication/oidc/KeycloakClient.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/iam/authentication/oidc/KeycloakClient.scala @@ -1,7 +1,6 @@ package io.iohk.atala.iam.authentication.oidc -import org.keycloak.authorization.client.AuthzClient -import org.keycloak.authorization.client.{Configuration => KeycloakAuthzConfig} +import org.keycloak.authorization.client.{AuthzClient, Configuration as KeycloakAuthzConfig} import org.keycloak.representations.idm.authorization.AuthorizationRequest import zio.* import zio.http.* @@ -59,26 +58,28 @@ class KeycloakClientImpl(client: AuthzClient, httpClient: Client, override val k // TODO: support offline introspection // https://www.keycloak.org/docs/22.0.4/securing_apps/#_token_introspection_endpoint override def introspectToken(token: AccessToken): IO[KeycloakClientError, TokenIntrospection] = { - for { - response <- Client + (for { + url <- ZIO.fromEither(URL.decode(introspectionUrl)).orDie + response <- httpClient .request( - url = introspectionUrl, - method = Method.POST, - headers = baseFormHeaders ++ Headers( - Header.Authorization.Basic( - username = URLEncoder.encode(keycloakConfig.clientId, StandardCharsets.UTF_8), - password = URLEncoder.encode(keycloakConfig.clientSecret, StandardCharsets.UTF_8) - ) - ), - content = Body.fromURLEncodedForm( - Form( - FormField.simpleField("token", token.toString) + Request( + url = url, + method = Method.POST, + headers = baseFormHeaders ++ Headers( + Header.Authorization.Basic( + username = URLEncoder.encode(keycloakConfig.clientId, StandardCharsets.UTF_8), + password = URLEncoder.encode(keycloakConfig.clientSecret, StandardCharsets.UTF_8) + ) + ), + body = Body.fromURLEncodedForm( + Form( + FormField.simpleField("token", token.toString) + ) ) ) ) .logError("Fail to introspect token on keycloak.") .mapError(e => KeycloakClientError.UnexpectedError("Fail to introspect the token on keycloak.")) - .provide(ZLayer.succeed(httpClient)) body <- response.body.asString .logError("Fail parse keycloak introspection response.") .mapError(e => KeycloakClientError.UnexpectedError("Fail parse keycloak introspection response.")) @@ -92,29 +93,31 @@ class KeycloakClientImpl(client: AuthzClient, httpClient: Client, override val k ZIO.logError(s"Keycloak token introspection was unsucessful. Status: ${response.status}. Response: $body") *> ZIO.fail(KeycloakClientError.UnexpectedError("Token introspection was unsuccessful.")) } - } yield result + } yield result).provide(Scope.default) } override def getAccessToken(username: String, password: String): IO[KeycloakClientError, TokenResponse] = { - for { - response <- Client + (for { + url <- ZIO.fromEither(URL.decode(tokenUrl)).orDie + response <- httpClient .request( - url = tokenUrl, - method = Method.POST, - headers = baseFormHeaders, - content = Body.fromURLEncodedForm( - Form( - FormField.simpleField("grant_type", "password"), - FormField.simpleField("client_id", keycloakConfig.clientId), - FormField.simpleField("client_secret", keycloakConfig.clientSecret), - FormField.simpleField("username", username), - FormField.simpleField("password", password), + Request( + url = url, + method = Method.POST, + headers = baseFormHeaders, + body = Body.fromURLEncodedForm( + Form( + FormField.simpleField("grant_type", "password"), + FormField.simpleField("client_id", keycloakConfig.clientId), + FormField.simpleField("client_secret", keycloakConfig.clientSecret), + FormField.simpleField("username", username), + FormField.simpleField("password", password), + ) ) ) ) .logError("Fail to get the accessToken on keycloak.") .mapError(e => KeycloakClientError.UnexpectedError("Fail to get the accessToken on keycloak.")) - .provide(ZLayer.succeed(httpClient)) body <- response.body.asString .logError("Fail parse keycloak token response.") .mapError(e => KeycloakClientError.UnexpectedError("Fail parse keycloak token response.")) @@ -128,7 +131,7 @@ class KeycloakClientImpl(client: AuthzClient, httpClient: Client, override val k ZIO.logError(s"Keycloak token introspection was unsucessful. Status: ${response.status}. Response: $body") *> ZIO.fail(KeycloakClientError.UnexpectedError("Token introspection was unsuccessful.")) } - } yield result + } yield result).provide(Scope.default) } override def getRpt(accessToken: AccessToken): IO[KeycloakClientError, AccessToken] = From d73e97dd61d396f9bd73348d7ba26173b7f506be Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Fri, 19 Jan 2024 11:02:52 +0000 Subject: [PATCH 06/26] chore(prism-agent): format docker-compose YAML file Signed-off-by: David Poltorak --- .../docker-compose.yml | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/infrastructure/single-tenant-testing-stack/docker-compose.yml b/infrastructure/single-tenant-testing-stack/docker-compose.yml index b853bdf186..8fe1515585 100644 --- a/infrastructure/single-tenant-testing-stack/docker-compose.yml +++ b/infrastructure/single-tenant-testing-stack/docker-compose.yml @@ -141,15 +141,16 @@ services: retries: 5 extra_hosts: - "host.docker.internal:host-gateway" - command: [ - -Dcom.sun.management.jmxremote, - -Dcom.sun.management.jmxremote.port=9095, - -Dcom.sun.management.jmxremote.rmi.port=9095, - -Dcom.sun.management.jmxremote.ssl=false, - -Dcom.sun.management.jmxremote.local.only=true, - -Dcom.sun.management.jmxremote.authenticate=false, - -Djava.rmi.server.hostname=127.0.0.1 - ] + command: + [ + -Dcom.sun.management.jmxremote, + -Dcom.sun.management.jmxremote.port=9095, + -Dcom.sun.management.jmxremote.rmi.port=9095, + -Dcom.sun.management.jmxremote.ssl=false, + -Dcom.sun.management.jmxremote.local.only=true, + -Dcom.sun.management.jmxremote.authenticate=false, + -Djava.rmi.server.hostname=127.0.0.1, + ] ports: - 9095:9095 @@ -221,7 +222,7 @@ services: holder-oea-postgres-exporter: image: quay.io/prometheuscommunity/postgres-exporter ports: - - "9996:9187" + - "9996:9187" environment: - DATA_SOURCE_NAME=postgresql://postgres:postgres@issuer-db:5432/postgres?sslmode=disable @@ -282,15 +283,16 @@ services: retries: 5 extra_hosts: - "host.docker.internal:host-gateway" - command: [ - -Dcom.sun.management.jmxremote, - -Dcom.sun.management.jmxremote.port=9096, - -Dcom.sun.management.jmxremote.rmi.port=9096, - -Dcom.sun.management.jmxremote.ssl=false, - -Dcom.sun.management.jmxremote.local.only=true, - -Dcom.sun.management.jmxremote.authenticate=false, - -Djava.rmi.server.hostname=127.0.0.1 - ] + command: + [ + -Dcom.sun.management.jmxremote, + -Dcom.sun.management.jmxremote.port=9096, + -Dcom.sun.management.jmxremote.rmi.port=9096, + -Dcom.sun.management.jmxremote.ssl=false, + -Dcom.sun.management.jmxremote.local.only=true, + -Dcom.sun.management.jmxremote.authenticate=false, + -Djava.rmi.server.hostname=127.0.0.1, + ] ports: - 9096:9096 From c8e5d6f48a6005a0a9c9aec368af262186a4fa62 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Fri, 19 Jan 2024 11:04:18 +0000 Subject: [PATCH 07/26] chore(prism-agent): apply scalafmt to DidCommHttpServer Signed-off-by: David Poltorak --- .../atala/agent/server/DidCommHttpServer.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala index 6ed9677958..6d6649d164 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala @@ -60,13 +60,13 @@ object DidCommHttpServer { result .tapError(error => ZIO.logErrorCause("Error processing incoming DIDComm message", Cause.fail(error))) .catchAll { - case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) + case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) case _: DIDCommMessageParsingError => ZIO.succeed(Response.status(Status.BadRequest)) - case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) - case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) - case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) + case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) + case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) } } @@ -90,8 +90,7 @@ object DidCommHttpServer { // case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) // } - Routes(rootRoute).toHttpApp - + Routes(rootRoute).toHttpApp private[this] def extractFirstRecipientDid(jsonMessage: String): IO[ParsingFailure | DecodingFailure, String] = { val doc = parse(jsonMessage).getOrElse(Json.Null) From 2cce45d0fab4cd091593609b4088157c05e32f08 Mon Sep 17 00:00:00 2001 From: Benjamin Voiturier Date: Fri, 19 Jan 2024 13:33:23 +0100 Subject: [PATCH 08/26] fix(prism-agent): validate 'content-type' header of incoming DIDComm requests Signed-off-by: Benjamin Voiturier --- .../agent/server/DidCommHttpServer.scala | 51 +++++++++---------- .../agent/server/DidCommHttpServerError.scala | 1 + 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala index 6d6649d164..c0e2971a99 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServer.scala @@ -2,7 +2,11 @@ package io.iohk.atala.agent.server import io.circe.* import io.circe.parser.* -import io.iohk.atala.agent.server.DidCommHttpServerError.{DIDCommMessageParsingError, RequestBodyParsingError} +import io.iohk.atala.agent.server.DidCommHttpServerError.{ + DIDCommMessageParsingError, + InvalidContentTypeError, + RequestBodyParsingError +} import io.iohk.atala.agent.server.config.AppConfig import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError.{KeyNotFoundError, WalletNotFoundError} @@ -46,21 +50,33 @@ object DidCommHttpServer { } yield () } + private def validateContentType(req: Request) = { + import zio.http.Header.ContentType + for { + contentType <- ZIO + .fromOption(req.rawHeader(ContentType.name)) + .mapError(_ => InvalidContentTypeError(s"The '${ContentType.name}' header is required")) + _ <- + if (contentType.equalsIgnoreCase(MediaTypes.contentTypeEncrypted)) ZIO.unit + else ZIO.fail(InvalidContentTypeError(s"Unsupported '${ContentType.name}' header value: $contentType")) + } yield contentType + } + private def didCommServiceEndpoint: HttpApp[ DidOps & CredentialService & PresentationService & ConnectionService & ManagedDIDService & HttpClient & DIDResolver & DIDNonSecretStorage & AppConfig ] = val rootRoute = Method.POST / "" -> handler { (req: Request) => - val result = req.body.asString - .mapError(e => RequestBodyParsingError(e.getMessage)) - .flatMap { data => - webServerProgram(data) - } - .map(_ => Response.ok) + val result = for { + _ <- validateContentType(req) + body <- req.body.asString.mapError(e => RequestBodyParsingError(e.getMessage)) + _ <- webServerProgram(body) + } yield Response.ok result .tapError(error => ZIO.logErrorCause("Error processing incoming DIDComm message", Cause.fail(error))) .catchAll { case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) + case _: InvalidContentTypeError => ZIO.succeed(Response.status(Status.BadRequest)) case _: DIDCommMessageParsingError => ZIO.succeed(Response.status(Status.BadRequest)) case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) @@ -69,27 +85,6 @@ object DidCommHttpServer { case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) } } - -// HttpApp.collectZIO[Request] { -// case req @ Method.POST -> Root -// if req.rawHeader("content-type").fold(false) { _.equalsIgnoreCase(MediaTypes.contentTypeEncrypted) } => -// val result = for { -// data <- req.body.asString.mapError(e => RequestBodyParsingError(e.getMessage)) -// _ <- webServerProgram(data) -// } yield Response.ok -// -// result -// .tapError(error => ZIO.logErrorCause("Error processing incoming DIDComm message", Cause.fail(error))) -// .catchAll { -// case _: RequestBodyParsingError => ZIO.succeed(Response.status(Status.BadRequest)) -// case _: DIDCommMessageParsingError => ZIO.succeed(Response.status(Status.BadRequest)) -// case _: ParseResponse => ZIO.succeed(Response.status(Status.BadRequest)) -// case _: DIDSecretStorageError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) -// case _: ConnectionServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) -// case _: CredentialServiceError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) -// case _: PresentationError => ZIO.succeed(Response.status(Status.UnprocessableEntity)) -// } - Routes(rootRoute).toHttpApp private[this] def extractFirstRecipientDid(jsonMessage: String): IO[ParsingFailure | DecodingFailure, String] = { diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServerError.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServerError.scala index d66da61b72..aacbbe732e 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServerError.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/DidCommHttpServerError.scala @@ -3,6 +3,7 @@ package io.iohk.atala.agent.server sealed trait DidCommHttpServerError object DidCommHttpServerError { + case class InvalidContentTypeError(error: String) extends DidCommHttpServerError case class RequestBodyParsingError(error: String) extends DidCommHttpServerError case class DIDCommMessageParsingError(error: String) extends DidCommHttpServerError } From f9d6c3a91962feca67aea3fbc274e5583f778a2b Mon Sep 17 00:00:00 2001 From: Benjamin Voiturier Date: Fri, 19 Jan 2024 14:25:33 +0100 Subject: [PATCH 09/26] chore(prisma-gent): extract ZIO HTTP client params (timeouts + cp size) to agent's configuration file Signed-off-by: Benjamin Voiturier --- .../server/src/main/resources/application.conf | 8 ++++++++ .../scala/io/iohk/atala/agent/server/Main.scala | 16 +++++++++++----- .../atala/agent/server/config/AppConfig.scala | 3 +++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/prism-agent/service/server/src/main/resources/application.conf b/prism-agent/service/server/src/main/resources/application.conf index d9904d7b6d..ca259e9475 100644 --- a/prism-agent/service/server/src/main/resources/application.conf +++ b/prism-agent/service/server/src/main/resources/application.conf @@ -86,6 +86,14 @@ agent { publicEndpointUrl = "http://localhost:8090" publicEndpointUrl = ${?DIDCOMM_SERVICE_URL} } + httpClient { + connectionPoolSize = 0 + connectionPoolSize = ${?AGENT_HTTP_CLIENT_CONNECTION_POOL_SIZE} + idleTimeout = 2.seconds + idleTimeout = ${?AGENT_HTTP_CLIENT_IDLE_TIMEOUT} + connectionTimeout = 2.seconds + connectionTimeout = ${?AGENT_HTTP_CLIENT_CONNECTION_TIMEOUT} + } authentication { admin { token = "admin" diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala index 7ba050ff96..c653cfed14 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala @@ -87,11 +87,17 @@ object MainApp extends ZIOAppDefault { private val zioHttpClientLayer = { import zio.http.netty.NettyConfig import zio.http.{ConnectionPoolConfig, DnsResolver, ZClient} - (ZLayer.succeed( - ZClient.Config.default.copy( - connectionPool = ConnectionPoolConfig.Disabled, - idleTimeout = Some(2.seconds), - connectionTimeout = Some(2.seconds), + (ZLayer.fromZIO( + for { + appConfig <- ZIO.service[AppConfig].provide(SystemModule.configLayer) + } yield ZClient.Config.default.copy( + connectionPool = { + val cpSize = appConfig.agent.httpClient.connectionPoolSize + if (cpSize > 0) ConnectionPoolConfig.Fixed(cpSize) + else ConnectionPoolConfig.Disabled + }, + idleTimeout = Some(appConfig.agent.httpClient.idleTimeout), + connectionTimeout = Some(appConfig.agent.httpClient.connectionTimeout), ) ) ++ ZLayer.succeed(NettyConfig.default) ++ diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/config/AppConfig.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/config/AppConfig.scala index b759281148..aec28e50a7 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/config/AppConfig.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/config/AppConfig.scala @@ -122,6 +122,7 @@ final case class DefaultWalletConfig( final case class AgentConfig( httpEndpoint: HttpEndpointConfig, didCommEndpoint: DidCommEndpointConfig, + httpClient: HttpClientConfig, authentication: AuthenticationConfig, database: DatabaseConfig, verification: VerificationConfig, @@ -145,6 +146,8 @@ final case class DidCommEndpointConfig(http: HttpConfig, publicEndpointUrl: Stri final case class HttpConfig(port: Int) +final case class HttpClientConfig(connectionPoolSize: Int, idleTimeout: Duration, connectionTimeout: Duration) + final case class SecretStorageConfig( backend: SecretStorageBackend, vault: Option[VaultConfig], From c58d491418bf9bccb34cfe2a40677e8f97c185af Mon Sep 17 00:00:00 2001 From: Benjamin Voiturier Date: Fri, 19 Jan 2024 15:36:54 +0100 Subject: [PATCH 10/26] fix(prism-agent): ensure response processing by HTTP client is part of the active ZIO Scope Signed-off-by: Benjamin Voiturier --- .../service/HttpURIDereferencerImpl.scala | 4 +- .../agent/server/http/ZioHttpClient.scala | 8 +- .../scala/io/iohk/atala/ZioHttpTest.scala | 74 +++++++++++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala index 4dc1ac0097..05b48159e9 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala @@ -11,12 +11,11 @@ import java.nio.charset.StandardCharsets class HttpURIDereferencerImpl(client: Client) extends URIDereferencer { override def dereference(uri: URI): IO[URIDereferencerError, String] = { - for { + val program = for { url <- ZIO.fromOption(URL.fromURI(uri)).mapError(_ => ConnectionError(s"Invalid URI: $uri")) response <- client .request(Request(url = url)) .mapError(t => ConnectionError(t.getMessage)) - .provideSomeLayer(zio.Scope.default) body <- response.status match { case Status.Ok => response.body.asString.mapError(t => UnexpectedError(t.getMessage)) @@ -33,6 +32,7 @@ class HttpURIDereferencerImpl(client: Client) extends URIDereferencer { ZIO.fail(UnexpectedError(s"Unexpected response status: $status")) } } yield body + program.provideSomeLayer(zio.Scope.default) } } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala index ab41360394..652917d4fd 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/ZioHttpClient.scala @@ -13,11 +13,10 @@ object ZioHttpClient { class ZioHttpClient(client: zio.http.Client) extends HttpClient { override def get(url: String): Task[HttpResponse] = - for { + val program = for { url <- ZIO.fromEither(URL.decode(url)).orDie response <- client .request(Request(url = url)) - .provideSomeLayer(zio.Scope.default) .flatMap { response => response.headers.toSeq.map(e => e) response.body.asString @@ -30,9 +29,10 @@ class ZioHttpClient(client: zio.http.Client) extends HttpClient { ) } } yield response + program.provideSomeLayer(zio.Scope.default) def postDIDComm(url: String, data: String): Task[HttpResponse] = - for { + val program = for { url <- ZIO.fromEither(URL.decode(url)).orDie response <- client .request( @@ -43,7 +43,6 @@ class ZioHttpClient(client: zio.http.Client) extends HttpClient { body = Body.fromChunk(Chunk.fromArray(data.getBytes)) ) ) - .provideSomeLayer(zio.Scope.default) .flatMap { response => response.headers.toSeq.map(e => e) response.body.asString @@ -56,4 +55,5 @@ class ZioHttpClient(client: zio.http.Client) extends HttpClient { ) } } yield response + program.provideSomeLayer(zio.Scope.default) } diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala new file mode 100644 index 0000000000..c76f016073 --- /dev/null +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala @@ -0,0 +1,74 @@ +package io.iohk.atala + +import zio.* +import zio.http.* +import zio.http.ZClient.Config +import zio.http.netty.NettyConfig + +import java.net.URI +import java.time +import java.time.Instant + +object ZioHttpTest extends ZIOAppDefault { + + def effect(url: URL): URIO[Client & Scope, Unit] = (for { + start <- ZIO.succeed(Instant.now) + client <- ZIO.service[Client] + response <- client + .request( + Request( + url = url, // TODO make ERROR type + method = Method.GET, + headers = Headers("content-type" -> "application/didcomm-encrypted+json") + ) + ) +// .provideSomeLayer { +// implicit val trace: Trace = Trace.empty +// (ZLayer.succeed( +// Config.default.copy( +// connectionPool = ConnectionPoolConfig.Fixed(10), +// idleTimeout = Some(1.seconds), +// connectionTimeout = Some(1.seconds), +// ) +// ) ++ +// ZLayer.succeed(NettyConfig.default) ++ +// DnsResolver.default) >>> zio.http.Client.live +// } +// .provideSomeLayer(zio.Scope.default) + .flatMap { response => + response.body.asString + } + end <- ZIO.succeed(Instant.now) + _ <- ZIO.logInfo(s"Query duration => ${time.Duration.between(start, end).toMillis}") + } yield response).exit + .flatMap { + case Exit.Success(_) => + ZIO.unit + case Exit.Failure(cause) => + ZIO.logError(s"Failure => ${cause.squash}") *> ZIO.succeed(cause.squash.printStackTrace()) + } + + val count = 10 + + override def run = (for { + url <- ZIO.fromOption( + URL.fromURI(URI("http://127.0.0.1:8080/prism-agent/schema-registry/schemas/4e67f019-dceb-3c0f-ac8c-7bb90e2c4df6")) + ) + _ <- ZIO.logInfo("Sending request...") *> effect(url) *> ZIO.logInfo("Request sent!") + _ <- ZIO.logInfo("First request completed!! Other requests in 15 seconds") *> ZIO.sleep(15.seconds) + _ <- ZIO.foreachPar(1 to count)(_ => effect(url)).withParallelism(1) + } yield ()) + .provideSomeLayer { + implicit val trace: Trace = Trace.empty + (ZLayer.succeed( + Config.default.copy( + connectionPool = ConnectionPoolConfig.Disabled, + idleTimeout = Some(2.seconds), + connectionTimeout = Some(2.seconds), + ) + ) ++ + ZLayer.succeed(NettyConfig.default) ++ + DnsResolver.default) >>> zio.http.Client.live + } + // .provideSomeLayer(zio.Scope.default) +} From 91fec3584e9527a418a7a03307225256ff3ba118 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Tue, 23 Jan 2024 14:01:55 +0000 Subject: [PATCH 11/26] feat(prism-agent): add security.md for security vuln reporting Signed-off-by: David Poltorak --- SECURITY.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index bd1e21eff2..e0b1a6d776 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,6 +12,4 @@ Report security bugs in third-party modules to the person or team maintaining th ## Security Notification Process -Security notifications will be sent through Discord - -# Security Policy +Security notifications will be sent to the dedicated `open-enterprise-agent` channel in the Hyper Ledger discord server (https://discordapp.com/channels/905194001349627914/1156985986174165032) From 2261fb520288f8d3da1e1288df7ebe0fef9e324c Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Tue, 23 Jan 2024 14:06:54 +0000 Subject: [PATCH 12/26] chore: update link to discord channel for security notices Signed-off-by: David Poltorak --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index e0b1a6d776..0d51f8a6c2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,4 +12,4 @@ Report security bugs in third-party modules to the person or team maintaining th ## Security Notification Process -Security notifications will be sent to the dedicated `open-enterprise-agent` channel in the Hyper Ledger discord server (https://discordapp.com/channels/905194001349627914/1156985986174165032) +Security notifications will be sent to the dedicated [open-enterprise-agent-security-notices](https://discordapp.com/channels/905194001349627914/1199354196944748614) channel in the Hyper Ledger discord server From 8e38468c3995f66d20e5ce0884d025bf36268c5c Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:22:41 +0000 Subject: [PATCH 13/26] fix(prism-agent): restore metric test integration test Signed-off-by: David Poltorak --- .../src/test/kotlin/features/system/SystemSteps.kt | 12 ++++++------ .../features/system/metrics_endpoint.feature | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt b/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt index 819c98abf0..c9db4a12c2 100644 --- a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt @@ -29,7 +29,7 @@ class SystemSteps { ) } - @When("{actor} Issuer makes a request to the metrics endpoint") + @When("{actor} makes a request to the metrics endpoint") fun actorRequestsMetricEndpoint(actor: Actor) { actor.attemptsTo( Get.resource("/_system/metrics") @@ -39,13 +39,13 @@ class SystemSteps { ) } - @Then("{actor} Issuer sees that the metrics contain background job stats") + @Then("{actor} sees that the metrics contain background job stats") fun actorSeesMetrics(actor: Actor) { - val metricsResponse = SerenityRest.lastResponse().get() + val metricsResponse = SerenityRest.lastResponse() actor.attemptsTo( - Ensure.that(metricsResponse).contains("present_proof_flow_did_com_exchange_job_ms_gauge"), - Ensure.that(metricsResponse).contains("connection_flow_did_com_exchange_job_ms_gauge"), - Ensure.that(metricsResponse).contains("issuance_flow_did_com_exchange_job_ms_gauge") + Ensure.that(metricsResponse.body.asString()).contains("present_proof_flow_did_com_exchange_job_ms_gauge"), + Ensure.that(metricsResponse.body.asString()).contains("connection_flow_did_com_exchange_job_ms_gauge"), + Ensure.that(metricsResponse.body.asString()).contains("issuance_flow_did_com_exchange_job_ms_gauge") ) } diff --git a/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature index 8c002e8d2d..aa1f78b94a 100644 --- a/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature +++ b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature @@ -1,6 +1,6 @@ @system @smoke -Feature: Agent Health Endpoint +Feature: Metrics Endpoint -# Scenario: Background job metrics are produced by the service and available to scrape -# When Issuer makes a request to the metrics endpoint -# Then Issuer sees that the metrics contain background job stats +Scenario: Background job metrics are produced by the service and available to scrape + When Issuer makes a request to the metrics endpoint + Then Issuer sees that the metrics contain background job stats From b19d7f3ed94200e6c3aec2b4d001a39b69c5b5b2 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:37:10 +0000 Subject: [PATCH 14/26] chore(prism-agent): shorten scenario name to meet linter rules Signed-off-by: David Poltorak --- .../src/test/resources/features/system/metrics_endpoint.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature index aa1f78b94a..0f7bef9139 100644 --- a/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature +++ b/tests/integration-tests/src/test/resources/features/system/metrics_endpoint.feature @@ -1,6 +1,6 @@ @system @smoke Feature: Metrics Endpoint -Scenario: Background job metrics are produced by the service and available to scrape +Scenario: Background job metrics are available to scrape When Issuer makes a request to the metrics endpoint Then Issuer sees that the metrics contain background job stats From 4eb157477016dd60ee7c1b6079bebf6254cba56e Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:40:31 +0000 Subject: [PATCH 15/26] chore(prism-agent): add linting hint for __ENV Signed-off-by: David Poltorak --- .../atala-performance-tests-k6/src/common/Config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts index 71ae4c27f9..ef7f5ac504 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts @@ -1,3 +1,5 @@ +/*global __ENV*/ + /** * Maximum number of iterations for the waiting loop. * If not provided, the default value is 100. @@ -6,7 +8,7 @@ export const WAITING_LOOP_MAX_ITERATIONS = Number(__ENV.MY_USER_AGENT) || 100; /** * Pause interval in seconds for each iteration of the waiting loop. - * If not provided, the default value is 1 second. + * If not provided, the default value is 10 milliseconds. */ export const WAITING_LOOP_PAUSE_INTERVAL = Number(__ENV.WAITING_LOOP_PAUSE_INTERVAL) || 0.1; From dced290879e0f14faf3cf661bc4e6d760d0d2b85 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:53:44 +0000 Subject: [PATCH 16/26] chore(prism-agent): adjusted linting rulest to exclude ext k6chaijs Signed-off-by: David Poltorak --- .mega-linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.mega-linter.yml b/.mega-linter.yml index 86e164326a..3a9cc05e47 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -49,3 +49,4 @@ SQL_SQL_LINT_ARGUMENTS: -d postgres --ignore-errors=postgres-invalid-alter-optio YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_PRETTIER_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_V8R_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" +JAVASCRIPT_STANDARD_FILTER_REGEX_EXCLUDE: "tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js" \ No newline at end of file From b2c3d83371a965a989200f515fb3f2dce8477f87 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:54:29 +0000 Subject: [PATCH 17/26] feat(prism-agent): remove unused imports and fix interpolation Signed-off-by: David Poltorak --- .../atala-performance-tests-k6/src/common/ConnectionService.ts | 3 +-- .../src/common/CredentialsService.ts | 2 +- .../atala-performance-tests-k6/src/common/DidService.ts | 1 - .../atala-performance-tests-k6/src/common/ProofsService.ts | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts index bec09da9f8..4361a5c199 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts @@ -4,7 +4,6 @@ import { Connection, ConnectionInvitation, ConnectionStateEnum } from "@input-ou import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import { HttpService, statusChangeTimeouts } from "./HttpService"; import { sleep, fail } from "k6"; -import {RefinedResponse, ResponseType} from "k6/http"; /** * A service class for managing connections in the application. @@ -72,7 +71,7 @@ export class ConnectionService extends HttpService { } while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS); if (state !== requiredState) { statusChangeTimeouts.add(1) - fail("Connection state is ${state}, required ${requiredState}"); + fail(`Connection state is ${state}, required ${requiredState}`); } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts index 0ce9290275..d2733bd5ae 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts @@ -1,6 +1,6 @@ import {fail, sleep} from "k6"; import { HttpService, statusChangeTimeouts } from "./HttpService"; -import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; +import {ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL} from "./Config"; import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; import { crypto } from "k6/experimental/webcrypto"; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts index 8416147099..b0c5bddd41 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts @@ -6,7 +6,6 @@ import { CreateManagedDIDResponse, DIDDocument, DidOperationSubmission, - IssueCredentialRecord, ManagedDID } from "@input-output-hk/prism-typescript-client"; import {fail, sleep} from "k6"; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts index e219d44e71..2624de0713 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts @@ -1,6 +1,6 @@ import { HttpService, statusChangeTimeouts } from "./HttpService"; import {fail, sleep} from "k6"; -import {Connection, IssueCredentialRecord, PresentationStatus} from "@input-output-hk/prism-typescript-client"; +import {Connection, PresentationStatus} from "@input-output-hk/prism-typescript-client"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import vu from "k6/execution"; From f5eef4d8ade71b7d9ad111b1d71dc9ff228c35eb Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 11:54:50 +0000 Subject: [PATCH 18/26] chore(prism-agent): restore schemaId in credential service Signed-off-by: David Poltorak --- .../atala-performance-tests-k6/src/common/CredentialsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts index d2733bd5ae..1bbeb291ee 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts @@ -4,7 +4,6 @@ import {ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERV import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; import { crypto } from "k6/experimental/webcrypto"; - /** * A service class for managing credentials in the application. * Extends the HttpService class. @@ -22,6 +21,7 @@ export class CredentialsService extends HttpService { "claims": { "emailAddress": "${crypto.randomUUID()}-@atala.io", "familyName": "Test", + "schemaId": "${ISSUER_AGENT_URL.replace("localhost", "host.docker.internal")}/schema-registry/schemas/${schema.guid}", "dateOfIssuance": "${new Date()}", "drivingLicenseID": "Test", "drivingClass": 1 From d9f72ca94203edc6d1c09339ae0a8f84c5711620 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 12:17:59 +0000 Subject: [PATCH 19/26] chore(prism-agent): remove comments which don't meet linting rule Signed-off-by: David Poltorak --- .../single-tenant-testing-stack/docker-compose.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/infrastructure/single-tenant-testing-stack/docker-compose.yml b/infrastructure/single-tenant-testing-stack/docker-compose.yml index 8fe1515585..d153375e02 100644 --- a/infrastructure/single-tenant-testing-stack/docker-compose.yml +++ b/infrastructure/single-tenant-testing-stack/docker-compose.yml @@ -314,8 +314,3 @@ volumes: holder_pg_data_db: node_pg_data_db: pgadmin: -# Temporary commit network setting due to e2e CI bug -# to be enabled later after debugging -#networks: -# default: -# name: ${NETWORK} From 889e41518d59fdd8dfdc4e04821301b49695943e Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 12:20:02 +0000 Subject: [PATCH 20/26] chore: add newline at end of megalinter file Signed-off-by: David Poltorak --- .mega-linter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.mega-linter.yml b/.mega-linter.yml index 3a9cc05e47..8c2a84e669 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -49,4 +49,5 @@ SQL_SQL_LINT_ARGUMENTS: -d postgres --ignore-errors=postgres-invalid-alter-optio YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_PRETTIER_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_V8R_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" -JAVASCRIPT_STANDARD_FILTER_REGEX_EXCLUDE: "tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js" \ No newline at end of file +JAVASCRIPT_STANDARD_FILTER_REGEX_EXCLUDE: "tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js" + From ce87790bcf99d476f0a4bfdac84d3a8e858c6db8 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 15:26:34 +0000 Subject: [PATCH 21/26] ci: set node variables to reduce delay in publish for perf test Signed-off-by: David Poltorak --- .github/workflows/performance-tests.yml | 3 +++ .mega-linter.yml | 1 - infrastructure/shared/docker-compose.yml | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index a92646eac4..53c1d9ecd3 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -64,6 +64,9 @@ jobs: API_KEY_ENABLED: true DOCKERHOST: "host.docker.internal" PG_PORT: 5432 + NODE_REFRESH_AND_SUBMIT_PERIOD: 1s + NODE_MOVE_SCHEDULED_TO_PENDING_PERIOD: 1s + NODE_WALLET_MAX_TPS: 1000 uses: isbang/compose-action@v1.4.1 with: compose-file: "./infrastructure/shared/docker-compose.yml" diff --git a/.mega-linter.yml b/.mega-linter.yml index 8c2a84e669..fac5888c12 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -50,4 +50,3 @@ YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_PRETTIER_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" YAML_V8R_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" JAVASCRIPT_STANDARD_FILTER_REGEX_EXCLUDE: "tests/performance-tests/atala-performance-tests-k6/src/k6chaijs.js" - diff --git a/infrastructure/shared/docker-compose.yml b/infrastructure/shared/docker-compose.yml index 2c211ca67a..bb44b8577a 100644 --- a/infrastructure/shared/docker-compose.yml +++ b/infrastructure/shared/docker-compose.yml @@ -47,6 +47,9 @@ services: image: ghcr.io/input-output-hk/prism-node:${PRISM_NODE_VERSION} environment: NODE_PSQL_HOST: db:5432 + NODE_REFRESH_AND_SUBMIT_PERIOD: + NODE_MOVE_SCHEDULED_TO_PENDING_PERIOD: + NODE_WALLET_MAX_TPS: depends_on: db: condition: service_healthy From 8127502ef5de5ff190ecbe675279d880915faca9 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 15:47:40 +0000 Subject: [PATCH 22/26] chore: increasing acceptable group duration for perf test (issuance) Signed-off-by: David Poltorak --- .../src/tests/flows/present-proof-flow-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts index 8670bdb01d..42597fcce3 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts @@ -9,7 +9,7 @@ export const localOptions: Options = { thresholds: { "group_duration{group:::Holder connects with Issuer}": ["avg < 5000"], "group_duration{group:::Issuer creates credential offer for Holder}": [ - "avg < 5000", + "avg < 10000", ], "group_duration{group:::Holder connects with Verifier}": ["avg < 5000"], "group_duration{group:::Verifier requests proof from Holder}": [ From 5125da9e0b55a0353adfb0c84c53b0bac15eeb99 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Wed, 24 Jan 2024 16:40:54 +0000 Subject: [PATCH 23/26] chore: increase all acceptable group durations for smoke test Signed-off-by: David Poltorak --- .../src/tests/flows/connection-flow-test.ts | 8 ++++---- .../src/tests/flows/issuance-flow-test.ts | 8 ++++---- .../src/tests/flows/present-proof-flow-test.ts | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts index 8b86b10623..ad214635a5 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts @@ -6,16 +6,16 @@ import merge from "ts-deepmerge"; export const localOptions: Options = { thresholds: { "group_duration{group:::Issuer initiates connection with Holder}": [ - "avg<5000", + "avg<10000", ], "group_duration{group:::Holder accepts connection with Issuer}": [ - "avg<5000", + "avg<10000", ], "group_duration{group:::Issuer finalizes connection with Holder}": [ - "avg<5000", + "avg<10000", ], "group_duration{group:::Holder finalizes connection with Issuer}": [ - "avg<5000", + "avg<10000", ], }, }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts index 28c59ac5d9..ac7f2aba65 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts @@ -8,15 +8,15 @@ import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - "group_duration{group:::Issuer connects with Holder}": ["avg < 5000"], + "group_duration{group:::Issuer connects with Holder}": ["avg < 10000"], "group_duration{group:::Issuer creates credential offer for Holder}": [ - "avg < 5000", + "avg < 10000", ], "group_duration{group:::Issuer issues credential to Holder}": [ - "avg < 5000", + "avg < 10000", ], "group_duration{group:::Holder receives credential from Issuer}": [ - "avg < 5000", + "avg < 10000", ], }, }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts index 42597fcce3..0acc502fd7 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts @@ -7,13 +7,13 @@ import { describe } from "../../k6chaijs.js"; export const localOptions: Options = { thresholds: { - "group_duration{group:::Holder connects with Issuer}": ["avg < 5000"], + "group_duration{group:::Holder connects with Issuer}": ["avg < 10000"], "group_duration{group:::Issuer creates credential offer for Holder}": [ "avg < 10000", ], - "group_duration{group:::Holder connects with Verifier}": ["avg < 5000"], + "group_duration{group:::Holder connects with Verifier}": ["avg < 10000"], "group_duration{group:::Verifier requests proof from Holder}": [ - "avg < 5000", + "avg < 10000", ], }, }; From 1e7096c5f7ae17c44c5cfb5e91ff8c2777a1fa53 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 25 Jan 2024 09:45:10 +0000 Subject: [PATCH 24/26] chore: remove commented out code to improve quality Signed-off-by: David Poltorak --- .../agent/notification/WebhookPublisher.scala | 7 +------ .../src/test/scala/io/iohk/atala/ZioHttpTest.scala | 14 -------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala index e075927ce1..652535462c 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala @@ -111,14 +111,9 @@ class WebhookPublisher( resp <- if response.status.isSuccess then ZIO.unit else { -// val err = response match { -// case Response.GetError(error) => Some(error) -// case _ => None -// } ZIO.fail( UnexpectedError( - s"Failed" -// s"Unsuccessful webhook response: [status: ${response.status} [error: ${err.getOrElse("none")}]" + s"Failed - Unsuccessful webhook response: [status: ${response.status}]" //TODO Restore error message in this unexpected error reporting ) ) } diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala index c76f016073..c218bb0243 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/ZioHttpTest.scala @@ -22,19 +22,6 @@ object ZioHttpTest extends ZIOAppDefault { headers = Headers("content-type" -> "application/didcomm-encrypted+json") ) ) -// .provideSomeLayer { -// implicit val trace: Trace = Trace.empty -// (ZLayer.succeed( -// Config.default.copy( -// connectionPool = ConnectionPoolConfig.Fixed(10), -// idleTimeout = Some(1.seconds), -// connectionTimeout = Some(1.seconds), -// ) -// ) ++ -// ZLayer.succeed(NettyConfig.default) ++ -// DnsResolver.default) >>> zio.http.Client.live -// } -// .provideSomeLayer(zio.Scope.default) .flatMap { response => response.body.asString } @@ -70,5 +57,4 @@ object ZioHttpTest extends ZIOAppDefault { ZLayer.succeed(NettyConfig.default) ++ DnsResolver.default) >>> zio.http.Client.live } - // .provideSomeLayer(zio.Scope.default) } From 05050e8f315fb8b184f4ca772ed2456382992485 Mon Sep 17 00:00:00 2001 From: David Poltorak Date: Thu, 25 Jan 2024 10:25:54 +0000 Subject: [PATCH 25/26] chore: run scalafmt Signed-off-by: David Poltorak --- .../io/iohk/atala/agent/notification/WebhookPublisher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala index 652535462c..c88aa35806 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/notification/WebhookPublisher.scala @@ -113,7 +113,7 @@ class WebhookPublisher( else { ZIO.fail( UnexpectedError( - s"Failed - Unsuccessful webhook response: [status: ${response.status}]" //TODO Restore error message in this unexpected error reporting + s"Failed - Unsuccessful webhook response: [status: ${response.status}]" // TODO Restore error message in this unexpected error reporting ) ) } From 53ae046a4346fc67f64469b9e7ffeb05f496c81b Mon Sep 17 00:00:00 2001 From: Benjamin Voiturier Date: Thu, 25 Jan 2024 12:18:42 +0100 Subject: [PATCH 26/26] chore(prism-agent): improve perf when handling HTTP URI dereferencer error case Signed-off-by: Benjamin Voiturier --- .../atala/pollux/core/service/HttpURIDereferencerImpl.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala index 05b48159e9..02ecb393e6 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/HttpURIDereferencerImpl.scala @@ -24,8 +24,8 @@ class HttpURIDereferencerImpl(client: Client) extends URIDereferencer { case status if status.isError => response.body.asStream .take(1024) // Only take the first 1024 bytes from the response body (if any). - .runFold(Array.empty[Byte])(_ :+ _) - .map(new String(_, StandardCharsets.UTF_8)) + .runCollect + .map(c => new String(c.toArray, StandardCharsets.UTF_8)) .orDie .flatMap(errorMessage => ZIO.fail(UnexpectedError(s"HTTP response error: $status - $errorMessage"))) case status =>