From ded6d3168f81c6a1afcb811a17e331ccd606eb1d Mon Sep 17 00:00:00 2001 From: Moritz Johner Date: Fri, 27 Sep 2019 02:54:41 +0200 Subject: [PATCH] feat: basic metrics (#147) * feat: basic metrics Signed-off-by: Moritz Johner * chore: update chart docs & env vars --- README.md | 11 + bin/daemon.js | 15 + charts/kubernetes-external-secrets/README.md | 2 + .../kubernetes-external-secrets/values.yaml | 2 + config/environment.js | 3 + lib/daemon.js | 3 + lib/metrics-server.js | 54 ++ lib/metrics-server.test.js | 61 +++ lib/metrics.js | 37 ++ lib/metrics.test.js | 32 ++ lib/poller.js | 17 +- lib/poller.test.js | 17 + package-lock.json | 460 +++++++++++++++++- package.json | 7 +- 14 files changed, 715 insertions(+), 6 deletions(-) create mode 100644 lib/metrics-server.js create mode 100644 lib/metrics-server.test.js create mode 100644 lib/metrics.js create mode 100644 lib/metrics.test.js diff --git a/README.md b/README.md index aea165d4..e55c5ef7 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ The following table lists the configurable parameters of the `kubernetes-externa | Parameter | Description | Default | | ----------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | | `env.AWS_REGION` | Set AWS_REGION in Deployment Pod | `us-west-2` | +| `env.LOG_LEVEL` | Set the application log level | `info` | +| `env.METRICS_PORT` | Specify the port for the prometheus metrics server | `3001` | | `env.POLLER_INTERVAL_MILLISECONDS` | Set POLLER_INTERVAL_MILLISECONDS in Deployment Pod | `10000` | | `envVarsFromSecret.AWS_ACCESS_KEY_ID` | Set AWS_ACCESS_KEY_ID (from a secret) in Deployment Pod | | | `envVarsFromSecret.AWS_SECRET_ACCESS_KEY` | Set AWS_SECRET_ACCESS_KEY (from a secret) in Deployment Pod | | @@ -186,6 +188,15 @@ secretDescriptor: property: username ``` +## Metrics + +kubernetes-external-secrets exposes the following metrics over a prometheus endpoint: + +| Metric | Description | Example | +| ----------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `sync_calls` | This metric counts the number of sync calls by backend, secret name and status | `sync_calls{name="foo",namespace="example",backend="foo",status="success"} 1` | + + ## Development [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) is a tool that makes it easy to run a Kubernetes cluster locally. diff --git a/bin/daemon.js b/bin/daemon.js index 486a9167..2b9197c9 100755 --- a/bin/daemon.js +++ b/bin/daemon.js @@ -7,7 +7,10 @@ // with an exit code of 1, just like any uncaught exception. require('make-promises-safe') +const Prometheus = require('prom-client') const Daemon = require('../lib/daemon') +const MetricsServer = require('../lib/metrics-server') +const Metrics = require('../lib/metrics') const { getExternalSecretEvents } = require('../lib/external-secret') const { @@ -16,6 +19,7 @@ const { customResourceManager, customResourceManifest, logger, + metricsPort, pollerIntervalMilliseconds } = require('../config') @@ -33,16 +37,27 @@ async function main () { logger }) + const registry = Prometheus.register + const metrics = new Metrics({ registry }) + const daemon = new Daemon({ backends, externalSecretEvents, kubeClient, logger, + metrics, pollerIntervalMilliseconds }) + const metricsServer = new MetricsServer({ + port: metricsPort, + registry, + logger + }) + logger.info('starting app') daemon.start() + metricsServer.start() logger.info('successfully started app') } diff --git a/charts/kubernetes-external-secrets/README.md b/charts/kubernetes-external-secrets/README.md index 4b60b8f1..e89f035f 100644 --- a/charts/kubernetes-external-secrets/README.md +++ b/charts/kubernetes-external-secrets/README.md @@ -37,6 +37,8 @@ The following table lists the configurable parameters of the `kubernetes-externa | Parameter | Description | Default | | ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | | `env.AWS_REGION` | Set AWS_REGION in Deployment Pod | `us-west-2` | +| `env.LOG_LEVEL` | Set the application log level | `info` | +| `env.METRICS_PORT` | Specify the port for the prometheus metrics server | `3001` | | `env.POLLER_INTERVAL_MILLISECONDS` | Set POLLER_INTERVAL_MILLISECONDS in Deployment Pod | `10000` | | `envVarsFromSecret.AWS_ACCESS_KEY_ID` | Set AWS_ACCESS_KEY_ID (from a secret) in Deployment Pod | | | `envVarsFromSecret.AWS_SECRET_ACCESS_KEY` | Set AWS_SECRET_ACCESS_KEY (from a secret) in Deployment Pod | | diff --git a/charts/kubernetes-external-secrets/values.yaml b/charts/kubernetes-external-secrets/values.yaml index 3fc78cc3..2bdb10b9 100644 --- a/charts/kubernetes-external-secrets/values.yaml +++ b/charts/kubernetes-external-secrets/values.yaml @@ -6,6 +6,8 @@ env: AWS_REGION: us-west-2 POLLER_INTERVAL_MILLISECONDS: 10000 + LOG_LEVEL: info + METRICS_PORT: 3001 # Create environment variables from exists k8s secrets # envVarsFromSecret: diff --git a/config/environment.js b/config/environment.js index 4f672d54..ee7b0a89 100644 --- a/config/environment.js +++ b/config/environment.js @@ -21,8 +21,11 @@ const pollerIntervalMilliseconds = process.env.POLLER_INTERVAL_MILLISECONDS const logLevel = process.env.LOG_LEVEL || 'info' +const metricsPort = process.env.METRICS_PORT || 3001 + module.exports = { environment, pollerIntervalMilliseconds, + metricsPort, logLevel } diff --git a/lib/daemon.js b/lib/daemon.js index 2d1879e8..6ca21ae4 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -19,12 +19,14 @@ class Daemon { externalSecretEvents, kubeClient, logger, + metrics, pollerIntervalMilliseconds }) { this._backends = backends this._kubeClient = kubeClient this._externalSecretEvents = externalSecretEvents this._logger = logger + this._metrics = metrics this._pollerIntervalMilliseconds = pollerIntervalMilliseconds this._pollers = {} @@ -74,6 +76,7 @@ class Daemon { intervalMilliseconds: this._pollerIntervalMilliseconds, kubeClient: this._kubeClient, logger: this._logger, + metrics: this._metrics, namespace: descriptor.namespace, secretDescriptor: descriptor.secretDescriptor, ownerReference: descriptor.ownerReference diff --git a/lib/metrics-server.js b/lib/metrics-server.js new file mode 100644 index 00000000..1d151c95 --- /dev/null +++ b/lib/metrics-server.js @@ -0,0 +1,54 @@ +'use strict' + +const express = require('express') +const Prometheus = require('prom-client') + +/** MetricsServer class. */ +class MetricsServer { + /** + * Create Metrics Server + * @param {number} port - the port to listen on + * @param {Object} logger - Logger for logging stuff + * @param {Object} register - Prometheus registry that holds metric data + */ + constructor ({ port, logger, registry }) { + this._port = port + this._logger = logger + this._registry = registry + + this._app = express() + this._app.get('/metrics', (req, res) => { + res.set('Content-Type', Prometheus.register.contentType) + res.end(this._registry.metrics()) + }) + } + + /** + * Start the metrics server: Listen on a TCP port and serve metrics over HTTP + */ + start () { + return new Promise((resolve, reject) => { + this._server = this._app.listen(this._port, () => { + this._logger.info(`MetricsServer listening on port ${this._port}`) + resolve() + }) + this._app.on('error', err => reject(err)) + }) + } + + /** + * Stop the metrics server + */ + stop () { + return new Promise((resolve, reject) => { + this._server.close(err => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } +} + +module.exports = MetricsServer diff --git a/lib/metrics-server.test.js b/lib/metrics-server.test.js new file mode 100644 index 00000000..f5c8b7e4 --- /dev/null +++ b/lib/metrics-server.test.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('chai') +const sinon = require('sinon') +const Prometheus = require('prom-client') +const request = require('supertest') + +const MetricsServer = require('./metrics-server') +const Metrics = require('./metrics') + +describe('MetricsServer', () => { + let server + let loggerMock + let registry + let metrics + + beforeEach(async () => { + loggerMock = sinon.mock() + loggerMock.info = sinon.stub() + registry = new Prometheus.Registry() + metrics = new Metrics({ registry }) + + server = new MetricsServer({ + logger: loggerMock, + registry: registry, + port: 3918 + }) + + await server.start() + }) + + afterEach(async () => { + sinon.restore() + await server.stop() + }) + + it('start server to serve metrics', async () => { + metrics.observeSync({ + name: 'foo', + namespace: 'example', + backend: 'foo', + status: 'success' + }) + + metrics.observeSync({ + name: 'bar', + namespace: 'example', + backend: 'foo', + status: 'failed' + }) + + const res = await request('http://localhost:3918') + .get('/metrics') + .expect('Content-Type', Prometheus.register.contentType) + .expect(200) + + expect(res.text).to.have.string('sync_calls{name="foo",namespace="example",backend="foo",status="success"} 1') + expect(res.text).to.have.string('sync_calls{name="bar",namespace="example",backend="foo",status="failed"} 1') + }) +}) diff --git a/lib/metrics.js b/lib/metrics.js new file mode 100644 index 00000000..83095e0f --- /dev/null +++ b/lib/metrics.js @@ -0,0 +1,37 @@ +'use strict' + +const Prometheus = require('prom-client') + +/** Metrics class. */ +class Metrics { + /** + * Create Metrics object + */ + constructor ({ registry }) { + this._registry = registry + this._syncCalls = new Prometheus.Counter({ + name: 'sync_calls', + help: 'number of sync operations', + labelNames: ['name', 'namespace', 'backend', 'status'], + registers: [registry] + }) + } + + /** + * Observe the result a sync process + * @param {String} name - the name of the externalSecret + * @param {String} namespace - the namespace of the externalSecret + * @param {String} backend - the backend used to fetch the externalSecret + * @param {String} status - the result of the sync process: error|success + */ + observeSync ({ name, namespace, backend, status }) { + this._syncCalls.inc({ + name, + namespace, + backend, + status + }) + } +} + +module.exports = Metrics diff --git a/lib/metrics.test.js b/lib/metrics.test.js new file mode 100644 index 00000000..b3d92315 --- /dev/null +++ b/lib/metrics.test.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('chai') +const sinon = require('sinon') +const Prometheus = require('prom-client') + +const Metrics = require('./metrics') + +describe('Metrics', () => { + let registry + let metrics + + beforeEach(async () => { + registry = new Prometheus.Registry() + metrics = new Metrics({ registry }) + }) + + afterEach(async () => { + sinon.restore() + }) + + it('should store metrics', async () => { + metrics.observeSync({ + name: 'foo', + namespace: 'example', + backend: 'foo', + status: 'success' + }) + expect(registry.metrics()).to.have.string('sync_calls{name="foo",namespace="example",backend="foo",status="success"} 1') + }) +}) diff --git a/lib/poller.js b/lib/poller.js index 7e44a4f9..bfb5ebe2 100644 --- a/lib/poller.js +++ b/lib/poller.js @@ -30,7 +30,8 @@ class Poller { logger, namespace, secretDescriptor, - ownerReference + ownerReference, + metrics }) { this._backends = backends this._intervalMilliseconds = intervalMilliseconds @@ -39,6 +40,7 @@ class Poller { this._namespace = namespace this._secretDescriptor = secretDescriptor this._ownerReference = ownerReference + this._metrics = metrics this._interval = null } @@ -75,7 +77,20 @@ class Poller { await this._upsertKubernetesSecret() } catch (err) { this._logger.error(err, `failure while polling the secret ${this._secretDescriptor.name}`) + this._metrics.observeSync({ + name: this._secretDescriptor.name, + namespace: this._namespace, + backend: this._secretDescriptor.backendType, + status: 'error' + }) } + + this._metrics.observeSync({ + name: this._secretDescriptor.name, + namespace: this._namespace, + backend: this._secretDescriptor.backendType, + status: 'success' + }) } /** diff --git a/lib/poller.test.js b/lib/poller.test.js index e8fcc372..8c001fc7 100644 --- a/lib/poller.test.js +++ b/lib/poller.test.js @@ -10,6 +10,7 @@ describe('Poller', () => { let backendMock let kubeClientMock let loggerMock + let metricsMock let pollerFactory const ownerReference = { @@ -24,10 +25,13 @@ describe('Poller', () => { backendMock = sinon.mock() kubeClientMock = sinon.mock() loggerMock = sinon.mock() + metricsMock = sinon.mock() loggerMock.info = sinon.stub() loggerMock.error = sinon.stub() + metricsMock.observeSync = sinon.stub() + pollerFactory = (secretDescriptor = { backendType: 'fakeBackendType', name: 'fakeSecretName', @@ -40,6 +44,7 @@ describe('Poller', () => { backends: { fakeBackendType: backendMock }, + metrics: metricsMock, intervalMilliseconds: 5000, kubeClient: kubeClientMock, logger: loggerMock, @@ -162,6 +167,12 @@ describe('Poller', () => { await poller._poll() expect(loggerMock.info.calledWith(`running poll on the secret ${poller._secretDescriptor.name}`)).to.equal(true) + + expect(metricsMock.observeSync.getCall(0).args[0]).to.deep.equal({ + name: 'fakeSecretName1', + namespace: 'fakeNamespace', + backend: 'fakeBackendType', + status: 'success' }) expect(poller._upsertKubernetesSecret.calledWith()).to.equal(true) }) @@ -170,6 +181,12 @@ describe('Poller', () => { poller._upsertKubernetesSecret.throws(error) await poller._poll() + + expect(metricsMock.observeSync.getCall(0).args[0]).to.deep.equal({ + name: 'fakeSecretName1', + namespace: 'fakeNamespace', + backend: 'fakeBackendType', + status: 'error' }) expect(loggerMock.error.calledWith(error, `failure while polling the secret ${poller._secretDescriptor.name}`)).to.equal(true) }) }) diff --git a/package-lock.json b/package-lock.json index ad458fee..4c1e3d85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -273,6 +273,15 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, "acorn": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", @@ -408,6 +417,11 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", @@ -615,11 +629,53 @@ "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", "dev": true }, + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -695,6 +751,11 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1087,6 +1148,19 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, "conventional-changelog": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.9.tgz", @@ -1317,6 +1391,22 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -1566,6 +1656,11 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, "detect-indent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", @@ -1632,12 +1727,22 @@ "safer-buffer": "^2.1.0" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -1686,6 +1791,11 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2087,6 +2197,11 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, "events": { "version": "1.1.1", "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -2170,6 +2285,68 @@ } } }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2344,6 +2521,35 @@ } } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -2480,6 +2686,17 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -2489,6 +2706,11 @@ "map-cache": "^0.2.2" } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, "fs-access": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", @@ -3566,6 +3788,25 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -3580,7 +3821,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -3709,6 +3949,11 @@ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -4462,6 +4707,11 @@ "object-visit": "^1.0.0" } }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -4532,6 +4782,11 @@ } } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, "merge-source-map": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", @@ -4549,6 +4804,11 @@ } } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -4570,6 +4830,11 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -4729,8 +4994,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mute-stream": { "version": "0.0.7", @@ -4770,6 +5034,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, "neo-async": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", @@ -5130,6 +5399,14 @@ "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-3.0.2.tgz", "integrity": "sha512-dTzp80/y/da+um+i+sOucNqiPpwRL7M/xPwj7pH1TFA2/bqQ+OK2sJahSXbemEoLtPkHcFLyhLhLWZa9yW5+RA==" }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5418,6 +5695,11 @@ "json-parse-better-errors": "^1.0.1" } }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -5617,6 +5899,23 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prom-client": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz", + "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==", + "requires": { + "tdigest": "^0.1.1" + } + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -5675,6 +5974,22 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6032,6 +6347,59 @@ "semver": "^5.0.3" } }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6061,6 +6429,11 @@ } } }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, "shallow-clone": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", @@ -6628,6 +7001,11 @@ } } }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6686,6 +7064,45 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "supertest": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", + "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^3.8.3" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -6744,6 +7161,14 @@ } } }, + "tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "requires": { + "bintrees": "1.0.1" + } + }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -6878,6 +7303,11 @@ "repeat-string": "^1.6.1" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -6959,6 +7389,15 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -7060,6 +7499,11 @@ "crypto-random-string": "^1.0.0" } }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7185,6 +7629,11 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", @@ -7200,6 +7649,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index 33449279..9a745a82 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,13 @@ }, "dependencies": { "aws-sdk": "^2.433.0", + "express": "^4.17.1", "json-stream": "^1.0.0", "kubernetes-client": "^8.3.0", "lodash.clonedeep": "^4.5.0", "make-promises-safe": "^5.0.0", - "pino": "^5.12.0" + "pino": "^5.12.0", + "prom-client": "^11.5.3" }, "devDependencies": { "chai": "^4.1.2", @@ -50,7 +52,8 @@ "nodemon": "^1.18.10", "nyc": "^14.0.0", "sinon": "^7.3.1", - "standard-version": "^7.0.0" + "standard-version": "^7.0.0", + "supertest": "^4.0.2" }, "nyc": { "check-coverage": true,