From b9e5d1cd3c465aa25294ccde9724876bacefcea0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:38:12 +0000 Subject: [PATCH 01/36] Bump github.com/go-openapi/validate from 0.22.1 to 0.23.0 Bumps [github.com/go-openapi/validate](https://github.com/go-openapi/validate) from 0.22.1 to 0.23.0. - [Commits](https://github.com/go-openapi/validate/compare/v0.22.1...v0.23.0) --- updated-dependencies: - dependency-name: github.com/go-openapi/validate dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0abf5b1577..76aa4a2f4b 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,14 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 github.com/cespare/xxhash/v2 v2.2.0 github.com/go-kit/log v0.2.1 - github.com/go-openapi/analysis v0.22.0 + github.com/go-openapi/analysis v0.22.2 github.com/go-openapi/errors v0.21.0 github.com/go-openapi/loads v0.21.5 github.com/go-openapi/runtime v0.27.1 github.com/go-openapi/spec v0.20.14 github.com/go-openapi/strfmt v0.22.0 github.com/go-openapi/swag v0.22.9 - github.com/go-openapi/validate v0.22.4 + github.com/go-openapi/validate v0.23.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/gogo/protobuf v1.3.2 github.com/hashicorp/go-sockaddr v1.0.6 diff --git a/go.sum b/go.sum index 05e397a596..60f1a0a695 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,8 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.22.0 h1:wQ/d07nf78HNj4u+KiSY0sT234IAyePPbMgpUjUJQR0= -github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= +github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0= +github.com/go-openapi/analysis v0.22.2/go.mod h1:pDF4UbZsQTo/oNuRfAWWd4dAh4yuYf//LYorPTjrpvo= github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= @@ -172,8 +172,8 @@ github.com/go-openapi/strfmt v0.22.0 h1:Ew9PnEYc246TwrEspvBdDHS4BVKXy/AOVsfqGDgA github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= -github.com/go-openapi/validate v0.22.4 h1:5v3jmMyIPKTR8Lv9syBAIRxG6lY0RqeBPB1LKEijzk8= -github.com/go-openapi/validate v0.22.4/go.mod h1:qm6O8ZIcPVdSY5219468Jv7kBdGvkiZLPOmqnqTUZ2A= +github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw= +github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= From 946a3b4e77f97e809ff9a2fb84b60d079ce092dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:36:04 +0000 Subject: [PATCH 02/36] Bump @types/react from 18.0.28 to 18.2.51 in /ui/react-app (#3706) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.0.28 to 18.2.51. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 14 +++++++------- ui/react-app/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index f4b5c37cee..ab41a0aa47 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -19,7 +19,7 @@ "use-query-params": "^2.1.1" }, "devDependencies": { - "@types/react": "^18.0.0", + "@types/react": "^18.2.51", "@types/react-dom": "^18.0.0", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", @@ -1321,9 +1321,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "18.2.51", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.51.tgz", + "integrity": "sha512-XeoMaU4CzyjdRr3c4IQQtiH7Rpo18V07rYZUucEZQwOUEtGgTXv7e6igQiQ+xnV6MbMe1qjEmKdgMNnfppnXfg==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -9067,9 +9067,9 @@ "dev": true }, "@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "18.2.51", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.51.tgz", + "integrity": "sha512-XeoMaU4CzyjdRr3c4IQQtiH7Rpo18V07rYZUucEZQwOUEtGgTXv7e6igQiQ+xnV6MbMe1qjEmKdgMNnfppnXfg==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 149c5debae..04691f55c6 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -21,7 +21,7 @@ "use-query-params": "^2.1.1" }, "devDependencies": { - "@types/react": "^18.0.0", + "@types/react": "^18.2.51", "@types/react-dom": "^18.0.0", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", From d5d89b3fc06ede975832f4f14032bf786fb32e60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:36:13 +0000 Subject: [PATCH 03/36] Bump eslint-webpack-plugin from 3.2.0 to 4.0.1 in /ui/react-app (#3695) Bumps [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin) from 3.2.0 to 4.0.1. - [Release notes](https://github.com/webpack-contrib/eslint-webpack-plugin/releases) - [Changelog](https://github.com/webpack-contrib/eslint-webpack-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/eslint-webpack-plugin/compare/v3.2.0...v4.0.1) --- updated-dependencies: - dependency-name: eslint-webpack-plugin dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 248 +++++++++++++++++++++++++++++---- ui/react-app/package.json | 2 +- 2 files changed, 223 insertions(+), 27 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index ab41a0aa47..ea91e3e01f 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -33,7 +33,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-webpack-plugin": "^3.2.0", + "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", "style-loader": "^3.3.1", @@ -775,6 +775,35 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -1113,6 +1142,12 @@ "node": ">=14" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@tanstack/query-core": { "version": "4.24.10", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.24.10.tgz", @@ -1211,9 +1246,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", - "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -1274,6 +1309,30 @@ "@types/node": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1409,6 +1468,21 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.53.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", @@ -2382,6 +2456,21 @@ "node": ">=6.0" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -3600,26 +3689,26 @@ } }, "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-fUFcXpui/FftGx3NzvWgLZXlLbu+m74sUxGEgxgoxYcUtkIQbS6SdNNZkS99m5ycb23TfoNYrDpp1k/CK5j6Hw==", "dev": true, "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", + "@types/eslint": "^8.37.0", + "jest-worker": "^29.5.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^8.0.0", "webpack": "^5.0.0" } }, @@ -5140,18 +5229,36 @@ "node": ">=0.10.0" } }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { @@ -8690,6 +8797,29 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -8880,6 +9010,12 @@ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==" }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "@tanstack/query-core": { "version": "4.24.10", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.24.10.tgz", @@ -8957,9 +9093,9 @@ } }, "@types/eslint": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", - "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", "dev": true, "requires": { "@types/estree": "*", @@ -9020,6 +9156,30 @@ "@types/node": "*" } }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -9155,6 +9315,21 @@ "@types/node": "*" } }, + "@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.53.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", @@ -9872,6 +10047,12 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, "clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -10831,13 +11012,13 @@ "dev": true }, "eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-fUFcXpui/FftGx3NzvWgLZXlLbu+m74sUxGEgxgoxYcUtkIQbS6SdNNZkS99m5ycb23TfoNYrDpp1k/CK5j6Hw==", "dev": true, "requires": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", + "@types/eslint": "^8.37.0", + "jest-worker": "^29.5.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" @@ -11939,13 +12120,28 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "requires": { "@types/node": "*", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 04691f55c6..964f77a940 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -35,7 +35,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-webpack-plugin": "^3.2.0", + "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", "style-loader": "^3.3.1", From 0b40ac1fd6426f24d79c1c15e09e5431c91fe26b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:36:22 +0000 Subject: [PATCH 04/36] Bump eslint from 8.35.0 to 8.56.0 in /ui/react-app (#3692) Bumps [eslint](https://github.com/eslint/eslint) from 8.35.0 to 8.56.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.35.0...v8.56.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 329 +++++++++++++++++++-------------- ui/react-app/package.json | 2 +- 2 files changed, 190 insertions(+), 141 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index ea91e3e01f..d9ca8e2099 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -26,7 +26,7 @@ "css-loader": "^6.7.1", "dotenv-defaults": "^5.0.2", "esbuild-loader": "^2.20.0", - "eslint": "^8.20.0", + "eslint": "^8.56.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.0", @@ -47,6 +47,15 @@ "webpack-merge": "^5.8.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -710,15 +719,39 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", - "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -734,22 +767,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -770,9 +803,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@jest/schemas": { @@ -1671,6 +1704,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -1879,9 +1918,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3318,26 +3357,28 @@ } }, "node_modules/eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3345,23 +3386,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -3680,12 +3717,15 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-webpack-plugin": { @@ -3713,9 +3753,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -3723,17 +3763,20 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4325,9 +4368,9 @@ "dev": true }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4398,6 +4441,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -5285,16 +5334,6 @@ "node": ">=10" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5968,17 +6007,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -8271,15 +8310,6 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8344,6 +8374,12 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -8751,15 +8787,30 @@ "dev": true, "optional": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", - "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -8769,19 +8820,19 @@ } }, "@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" } }, @@ -8792,9 +8843,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "@jest/schemas": { @@ -9429,6 +9480,12 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -9621,9 +9678,9 @@ } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true }, "acorn-import-assertions": { @@ -10714,26 +10771,28 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -10741,30 +10800,26 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -11006,9 +11061,9 @@ } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "eslint-webpack-plugin": { @@ -11025,14 +11080,14 @@ } }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esquery": { @@ -11485,9 +11540,9 @@ "dev": true }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -11537,6 +11592,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -12163,12 +12224,6 @@ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "dev": true }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12678,17 +12733,17 @@ "dev": true }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "p-limit": { @@ -14335,12 +14390,6 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 964f77a940..d84c0c8711 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -28,7 +28,7 @@ "css-loader": "^6.7.1", "dotenv-defaults": "^5.0.2", "esbuild-loader": "^2.20.0", - "eslint": "^8.20.0", + "eslint": "^8.56.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.0", From e8084744f08328614a1e758afd391fd58ccc1a2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:36:31 +0000 Subject: [PATCH 05/36] Bump style-loader from 3.3.1 to 3.3.4 in /ui/react-app (#3689) Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 3.3.1 to 3.3.4. - [Release notes](https://github.com/webpack-contrib/style-loader/releases) - [Changelog](https://github.com/webpack-contrib/style-loader/blob/v3.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/style-loader/compare/v3.3.1...v3.3.4) --- updated-dependencies: - dependency-name: style-loader dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 14 +++++++------- ui/react-app/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index d9ca8e2099..6068e9e6d9 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -36,7 +36,7 @@ "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", - "style-loader": "^3.3.1", + "style-loader": "^3.3.4", "ts-loader": "^9.3.1", "ts-node": "^10.9.1", "typescript": "^4.7.4", @@ -7385,9 +7385,9 @@ } }, "node_modules/style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "dev": true, "engines": { "node": ">= 12.13.0" @@ -13761,9 +13761,9 @@ "dev": true }, "style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "dev": true, "requires": {} }, diff --git a/ui/react-app/package.json b/ui/react-app/package.json index d84c0c8711..762ba186ba 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -38,7 +38,7 @@ "eslint-webpack-plugin": "^4.0.1", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", - "style-loader": "^3.3.1", + "style-loader": "^3.3.4", "ts-loader": "^9.3.1", "ts-node": "^10.9.1", "typescript": "^4.7.4", From 8e7f3d236656c2bf649a07321121ef9a4e177328 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:50:34 +0000 Subject: [PATCH 06/36] Bump ts-loader from 9.4.2 to 9.5.1 in /ui/react-app (#3688) Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 9.4.2 to 9.5.1. - [Release notes](https://github.com/TypeStrong/ts-loader/releases) - [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md) - [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.4.2...v9.5.1) --- updated-dependencies: - dependency-name: ts-loader dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 37 +++++++++++++++++++++++++--------- ui/react-app/package.json | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index 6068e9e6d9..4b54c03155 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -37,7 +37,7 @@ "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", "style-loader": "^3.3.4", - "ts-loader": "^9.3.1", + "ts-loader": "^9.5.1", "ts-node": "^10.9.1", "typescript": "^4.7.4", "webpack": "^5.75.0", @@ -7593,15 +7593,16 @@ } }, "node_modules/ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -7611,6 +7612,15 @@ "webpack": "^5.0.0" } }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -13897,15 +13907,24 @@ "dev": true }, "ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "requires": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } } }, "ts-node": { diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 762ba186ba..13f57b0493 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -39,7 +39,7 @@ "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", "style-loader": "^3.3.4", - "ts-loader": "^9.3.1", + "ts-loader": "^9.5.1", "ts-node": "^10.9.1", "typescript": "^4.7.4", "webpack": "^5.75.0", From 6d02051f2b451f8f6a8d4d085c1540339fde8825 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Wed, 7 Feb 2024 09:43:03 +0000 Subject: [PATCH 07/36] Do not register compat metrics in amtool (#3713) There is no need to register these metrics in amtool, so use compat.NewMetrics(nil) instead of compat.RegisteredMetrics. Signed-off-by: George Robinson --- cli/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/root.go b/cli/root.go index e1e5ac59aa..a94db39188 100644 --- a/cli/root.go +++ b/cli/root.go @@ -61,7 +61,7 @@ func initMatchersCompat(_ *kingpin.ParseContext) error { if err != nil { kingpin.Fatalf("error parsing the feature flag list: %v\n", err) } - compat.InitFromFlags(logger, compat.RegisteredMetrics, featureConfig) + compat.InitFromFlags(logger, compat.NewMetrics(nil), featureConfig) return nil } From 5eca7d517032327ef33311c27e1dc15cdebc7d31 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Wed, 7 Feb 2024 10:43:19 +0100 Subject: [PATCH 08/36] Update common Prometheus files (#3712) Signed-off-by: prombot --- Makefile.common | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.common b/Makefile.common index bc2a07d728..5fd1782371 100644 --- a/Makefile.common +++ b/Makefile.common @@ -62,10 +62,10 @@ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= GOLANGCI_LINT_VERSION ?= v1.55.2 -# golangci-lint only supports linux, darwin and windows platforms on i386/amd64. +# golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) - ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) + ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386 arm64)) # If we're in CI and there is an Actions file, that means the linter # is being run in Actions, so we don't need to run it here. ifneq (,$(SKIP_GOLANGCI_LINT)) From 3d49ff83c74e253fc60b7659d952b24c59c44962 Mon Sep 17 00:00:00 2001 From: Philipp B Date: Wed, 7 Feb 2024 11:33:46 +0100 Subject: [PATCH 09/36] feat: implement webhook_url_file for discord and msteams (#3555) * feat: implement webhook_url_file for discord implements #3482 Signed-off-by: Philipp Born * feat: implement webhook_url_file for msteams implements #3536 Signed-off-by: Philipp Born --------- Signed-off-by: Philipp Born --- config/notifiers.go | 34 ++++++++++++++++++++++++---- docs/configuration.md | 4 ++++ notify/discord/discord.go | 16 ++++++++++++- notify/discord/discord_test.go | 41 ++++++++++++++++++++++++++++++++++ notify/msteams/msteams.go | 16 ++++++++++++- notify/msteams/msteams_test.go | 41 ++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 6 deletions(-) diff --git a/config/notifiers.go b/config/notifiers.go index 0759a573df..d79c8b5057 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -216,8 +216,9 @@ func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type DiscordConfig struct { NotifierConfig `yaml:",inline" json:",inline"` - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` + WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` Title string `yaml:"title,omitempty" json:"title,omitempty"` Message string `yaml:"message,omitempty" json:"message,omitempty"` @@ -227,7 +228,19 @@ type DiscordConfig struct { func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultDiscordConfig type plain DiscordConfig - return unmarshal((*plain)(c)) + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.WebhookURL == nil && c.WebhookURLFile == "" { + return fmt.Errorf("one of webhook_url or webhook_url_file must be configured") + } + + if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { + return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured") + } + + return nil } // EmailConfig configures notifications via mail. @@ -787,6 +800,7 @@ type MSTeamsConfig struct { NotifierConfig `yaml:",inline" json:",inline"` HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` + WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` Title string `yaml:"title,omitempty" json:"title,omitempty"` Summary string `yaml:"summary,omitempty" json:"summary,omitempty"` @@ -796,5 +810,17 @@ type MSTeamsConfig struct { func (c *MSTeamsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultMSTeamsConfig type plain MSTeamsConfig - return unmarshal((*plain)(c)) + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.WebhookURL == nil && c.WebhookURLFile == "" { + return fmt.Errorf("one of webhook_url or webhook_url_file must be configured") + } + + if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { + return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured") + } + + return nil } diff --git a/docs/configuration.md b/docs/configuration.md index 298faaa3f5..f5d911b7c0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -670,7 +670,9 @@ Discord notifications are sent via the [Discord webhook API](https://discord.com [ send_resolved: | default = true ] # The Discord webhook URL. +# webhook_url and webhook_url_file are mutually exclusive. webhook_url: +webhook_url_file: # Message title template. [ title: | default = '{{ template "discord.default.title" . }}' ] @@ -735,7 +737,9 @@ Microsoft Teams notifications are sent via the [Incoming Webhooks](https://learn [ send_resolved: | default = true ] # The incoming webhook URL. +# webhook_url and webhook_url_file are mutually exclusive. [ webhook_url: ] +[ webhook_url_file: ] # Message title template. [ title: | default = '{{ template "msteams.default.title" . }}' ] diff --git a/notify/discord/discord.go b/notify/discord/discord.go index 2fad87a938..69aff39957 100644 --- a/notify/discord/discord.go +++ b/notify/discord/discord.go @@ -17,7 +17,10 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http" + "os" + "strings" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -120,6 +123,17 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) color = colorGreen } + var url string + if n.conf.WebhookURL != nil { + url = n.conf.WebhookURL.String() + } else { + content, err := os.ReadFile(n.conf.WebhookURLFile) + if err != nil { + return false, fmt.Errorf("read webhook_url_file: %w", err) + } + url = strings.TrimSpace(string(content)) + } + w := webhook{ Embeds: []webhookEmbed{{ Title: title, @@ -133,7 +147,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - resp, err := notify.PostJSON(ctx, n.client, n.webhookURL.String(), &payload) + resp, err := notify.PostJSON(ctx, n.client, url, &payload) if err != nil { return true, notify.RedactURL(err) } diff --git a/notify/discord/discord_test.go b/notify/discord/discord_test.go index 142a9a608e..a571ffffe0 100644 --- a/notify/discord/discord_test.go +++ b/notify/discord/discord_test.go @@ -20,6 +20,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "testing" "time" @@ -127,3 +128,43 @@ func TestDiscordTemplating(t *testing.T) { }) } } + +func TestDiscordRedactedURL(t *testing.T) { + ctx, u, fn := test.GetContextWithCancelingURL() + defer fn() + + secret := "secret" + notifier, err := New( + &config.DiscordConfig{ + WebhookURL: &config.SecretURL{URL: u}, + HTTPConfig: &commoncfg.HTTPClientConfig{}, + }, + test.CreateTmpl(t), + log.NewNopLogger(), + ) + require.NoError(t, err) + + test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret) +} + +func TestDiscordReadingURLFromFile(t *testing.T) { + ctx, u, fn := test.GetContextWithCancelingURL() + defer fn() + + f, err := os.CreateTemp("", "webhook_url") + require.NoError(t, err, "creating temp file failed") + _, err = f.WriteString(u.String() + "\n") + require.NoError(t, err, "writing to temp file failed") + + notifier, err := New( + &config.DiscordConfig{ + WebhookURLFile: f.Name(), + HTTPConfig: &commoncfg.HTTPClientConfig{}, + }, + test.CreateTmpl(t), + log.NewNopLogger(), + ) + require.NoError(t, err) + + test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String()) +} diff --git a/notify/msteams/msteams.go b/notify/msteams/msteams.go index 73994ee08d..d71e108144 100644 --- a/notify/msteams/msteams.go +++ b/notify/msteams/msteams.go @@ -17,8 +17,11 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "net/http" + "os" + "strings" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -113,6 +116,17 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) color = colorGreen } + var url string + if n.conf.WebhookURL != nil { + url = n.conf.WebhookURL.String() + } else { + content, err := os.ReadFile(n.conf.WebhookURLFile) + if err != nil { + return false, fmt.Errorf("read webhook_url_file: %w", err) + } + url = strings.TrimSpace(string(content)) + } + t := teamsMessage{ Context: "http://schema.org/extensions", Type: "MessageCard", @@ -127,7 +141,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - resp, err := n.postJSONFunc(ctx, n.client, n.webhookURL.String(), &payload) + resp, err := n.postJSONFunc(ctx, n.client, url, &payload) if err != nil { return true, notify.RedactURL(err) } diff --git a/notify/msteams/msteams_test.go b/notify/msteams/msteams_test.go index 909fde08fc..80f9439173 100644 --- a/notify/msteams/msteams_test.go +++ b/notify/msteams/msteams_test.go @@ -21,6 +21,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "testing" "time" @@ -192,3 +193,43 @@ func TestNotifier_Notify_WithReason(t *testing.T) { }) } } + +func TestMSTeamsRedactedURL(t *testing.T) { + ctx, u, fn := test.GetContextWithCancelingURL() + defer fn() + + secret := "secret" + notifier, err := New( + &config.MSTeamsConfig{ + WebhookURL: &config.SecretURL{URL: u}, + HTTPConfig: &commoncfg.HTTPClientConfig{}, + }, + test.CreateTmpl(t), + log.NewNopLogger(), + ) + require.NoError(t, err) + + test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret) +} + +func TestMSTeamsReadingURLFromFile(t *testing.T) { + ctx, u, fn := test.GetContextWithCancelingURL() + defer fn() + + f, err := os.CreateTemp("", "webhook_url") + require.NoError(t, err, "creating temp file failed") + _, err = f.WriteString(u.String() + "\n") + require.NoError(t, err, "writing to temp file failed") + + notifier, err := New( + &config.MSTeamsConfig{ + WebhookURLFile: f.Name(), + HTTPConfig: &commoncfg.HTTPClientConfig{}, + }, + test.CreateTmpl(t), + log.NewNopLogger(), + ) + require.NoError(t, err) + + test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String()) +} From f69a5086657bc69049f207945702a7436cb93840 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 8 Feb 2024 09:59:03 +0000 Subject: [PATCH 10/36] Remove metrics from compat package (#3714) This commit removes the metrics from the compat package in favour of the existing logging and the additional tools at hand, such as amtool, to validate Alertmanager configurations. Due to the global nature of the compat package, a consequence of config.Load, these metrics have proven to be less useful in practice than expected, both in Alertmanager and other projects such as Mimir. There are a number of reasons for this: 1. Because the compat package is global, these metrics cannot be reset each time config.Load is called, as in multi-tenant projects like Mimir loading a config for one tenant would reset the metrics for all tenants. This is also the reason the metrics are counters and not gauges. 2. Since the metrics are counters, it is difficult to create meaningful dashboards for Alertmanager as, unlike in Mimir, configurations are not reloaded at fixed intervals, and as such, operators cannot use rate to track configuration changes over time. In Alertmanager, there are much better tools available to validate that an Alertmanager configuration is compatible with the UTF-8 parser, including both the existing logging from Alertmanager server and amtool check-config. In other projects like Mimir, we can track configurations for individual tenants using log aggregation and storage systems such as Loki. This gives operators far more information than what is possible with the metrics, including the timestamp, input and ID of tenant configurations that are incompatible or have disagreement. Signed-off-by: George Robinson --- cli/root.go | 2 +- cmd/alertmanager/main.go | 2 +- matchers/compat/metrics.go | 60 ----------------------- matchers/compat/parse.go | 77 ++++++------------------------ matchers/compat/parse_test.go | 89 +++++++++-------------------------- silence/silence_test.go | 4 +- types/types_test.go | 4 +- 7 files changed, 43 insertions(+), 195 deletions(-) delete mode 100644 matchers/compat/metrics.go diff --git a/cli/root.go b/cli/root.go index a94db39188..69c1022c6a 100644 --- a/cli/root.go +++ b/cli/root.go @@ -61,7 +61,7 @@ func initMatchersCompat(_ *kingpin.ParseContext) error { if err != nil { kingpin.Fatalf("error parsing the feature flag list: %v\n", err) } - compat.InitFromFlags(logger, compat.NewMetrics(nil), featureConfig) + compat.InitFromFlags(logger, featureConfig) return nil } diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index cb225b2e16..b2938189d5 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -186,7 +186,7 @@ func run() int { level.Error(logger).Log("msg", "error parsing the feature flag list", "err", err) return 1 } - compat.InitFromFlags(logger, compat.RegisteredMetrics, ff) + compat.InitFromFlags(logger, ff) err = os.MkdirAll(*dataDir, 0o777) if err != nil { diff --git a/matchers/compat/metrics.go b/matchers/compat/metrics.go deleted file mode 100644 index 34b64099c8..0000000000 --- a/matchers/compat/metrics.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2023 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compat - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -const ( - OriginAPI = "api" - OriginConfig = "config" -) - -var DefaultOrigins = []string{ - OriginAPI, - OriginConfig, -} - -var RegisteredMetrics = NewMetrics(prometheus.DefaultRegisterer) - -type Metrics struct { - Total *prometheus.CounterVec - DisagreeTotal *prometheus.CounterVec - IncompatibleTotal *prometheus.CounterVec - InvalidTotal *prometheus.CounterVec -} - -func NewMetrics(r prometheus.Registerer) *Metrics { - m := &Metrics{ - Total: promauto.With(r).NewCounterVec(prometheus.CounterOpts{ - Name: "alertmanager_matchers_parse_total", - Help: "Total number of matcher inputs parsed, including invalid inputs.", - }, []string{"origin"}), - DisagreeTotal: promauto.With(r).NewCounterVec(prometheus.CounterOpts{ - Name: "alertmanager_matchers_disagree_total", - Help: "Total number of matcher inputs which produce different parsings (disagreement).", - }, []string{"origin"}), - IncompatibleTotal: promauto.With(r).NewCounterVec(prometheus.CounterOpts{ - Name: "alertmanager_matchers_incompatible_total", - Help: "Total number of matcher inputs that are incompatible with the UTF-8 parser.", - }, []string{"origin"}), - InvalidTotal: promauto.With(r).NewCounterVec(prometheus.CounterOpts{ - Name: "alertmanager_matchers_invalid_total", - Help: "Total number of matcher inputs that could not be parsed.", - }, []string{"origin"}), - } - return m -} diff --git a/matchers/compat/parse.go b/matchers/compat/parse.go index e6b2758b29..7aa4e2d95b 100644 --- a/matchers/compat/parse.go +++ b/matchers/compat/parse.go @@ -21,7 +21,6 @@ import ( "github.com/go-kit/log" "github.com/go-kit/log/level" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/featurecontrol" @@ -31,8 +30,8 @@ import ( var ( isValidLabelName = isValidClassicLabelName(log.NewNopLogger()) - parseMatcher = ClassicMatcherParser(log.NewNopLogger(), RegisteredMetrics) - parseMatchers = ClassicMatchersParser(log.NewNopLogger(), RegisteredMetrics) + parseMatcher = ClassicMatcherParser(log.NewNopLogger()) + parseMatchers = ClassicMatchersParser(log.NewNopLogger()) ) // IsValidLabelName returns true if the string is a valid label name. @@ -57,33 +56,26 @@ func Matchers(input, origin string) (labels.Matchers, error) { } // InitFromFlags initializes the compat package from the flagger. -func InitFromFlags(l log.Logger, m *Metrics, f featurecontrol.Flagger) { +func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { if f.ClassicMode() { isValidLabelName = isValidClassicLabelName(l) - parseMatcher = ClassicMatcherParser(l, m) - parseMatchers = ClassicMatchersParser(l, m) + parseMatcher = ClassicMatcherParser(l) + parseMatchers = ClassicMatchersParser(l) } else if f.UTF8StrictMode() { isValidLabelName = isValidUTF8LabelName(l) - parseMatcher = UTF8MatcherParser(l, m) - parseMatchers = UTF8MatchersParser(l, m) + parseMatcher = UTF8MatcherParser(l) + parseMatchers = UTF8MatchersParser(l) } else { isValidLabelName = isValidUTF8LabelName(l) - parseMatcher = FallbackMatcherParser(l, m) - parseMatchers = FallbackMatchersParser(l, m) + parseMatcher = FallbackMatcherParser(l) + parseMatchers = FallbackMatchersParser(l) } } // ClassicMatcherParser uses the pkg/labels parser to parse the matcher in // the input string. -func ClassicMatcherParser(l log.Logger, m *Metrics) ParseMatcher { +func ClassicMatcherParser(l log.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - defer func() { - lbs := prometheus.Labels{"origin": origin} - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) return labels.ParseMatcher(input) } @@ -91,15 +83,8 @@ func ClassicMatcherParser(l log.Logger, m *Metrics) ParseMatcher { // ClassicMatchersParser uses the pkg/labels parser to parse zero or more // matchers in the input string. It returns an error if the input is invalid. -func ClassicMatchersParser(l log.Logger, m *Metrics) ParseMatchers { +func ClassicMatchersParser(l log.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - defer func() { - lbs := prometheus.Labels{"origin": origin} - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) return labels.ParseMatchers(input) } @@ -107,15 +92,8 @@ func ClassicMatchersParser(l log.Logger, m *Metrics) ParseMatchers { // UTF8MatcherParser uses the new matchers/parse parser to parse the matcher // in the input string. If this fails it does not revert to the pkg/labels parser. -func UTF8MatcherParser(l log.Logger, m *Metrics) ParseMatcher { +func UTF8MatcherParser(l log.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - defer func() { - lbs := prometheus.Labels{"origin": origin} - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) @@ -127,15 +105,8 @@ func UTF8MatcherParser(l log.Logger, m *Metrics) ParseMatcher { // UTF8MatchersParser uses the new matchers/parse parser to parse zero or more // matchers in the input string. If this fails it does not revert to the // pkg/labels parser. -func UTF8MatchersParser(l log.Logger, m *Metrics) ParseMatchers { +func UTF8MatchersParser(l log.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - defer func() { - lbs := prometheus.Labels{"origin": origin} - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) return parse.Matchers(input) } @@ -144,15 +115,8 @@ func UTF8MatchersParser(l log.Logger, m *Metrics) ParseMatchers { // FallbackMatcherParser uses the new matchers/parse parser to parse zero or more // matchers in the string. If this fails it reverts to the pkg/labels parser and // emits a warning log line. -func FallbackMatcherParser(l log.Logger, m *Metrics) ParseMatcher { +func FallbackMatcherParser(l log.Logger) ParseMatcher { return func(input, origin string) (matcher *labels.Matcher, err error) { - lbs := prometheus.Labels{"origin": origin} - defer func() { - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) @@ -168,7 +132,6 @@ func FallbackMatcherParser(l log.Logger, m *Metrics) ParseMatcher { } // The input is valid in the pkg/labels parser, but not the matchers/parse // parser. This means the input is not forwards compatible. - m.IncompatibleTotal.With(lbs).Inc() suggestion := cMatcher.String() level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the old matchers parser as a fallback. To make this input compatible with the new parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatcher, nil @@ -176,7 +139,6 @@ func FallbackMatcherParser(l log.Logger, m *Metrics) ParseMatcher { // If the input is valid in both parsers, but produces different results, // then there is disagreement. if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatcher, cMatcher) { - m.DisagreeTotal.With(lbs).Inc() level.Warn(l).Log("msg", "Matchers input has disagreement", "input", input, "origin", origin) return cMatcher, nil } @@ -187,15 +149,8 @@ func FallbackMatcherParser(l log.Logger, m *Metrics) ParseMatcher { // FallbackMatchersParser uses the new matchers/parse parser to parse the // matcher in the input string. If this fails it falls back to the pkg/labels // parser and emits a warning log line. -func FallbackMatchersParser(l log.Logger, m *Metrics) ParseMatchers { +func FallbackMatchersParser(l log.Logger) ParseMatchers { return func(input, origin string) (matchers labels.Matchers, err error) { - lbs := prometheus.Labels{"origin": origin} - defer func() { - m.Total.With(lbs).Inc() - if err != nil { - m.InvalidTotal.With(lbs).Inc() - } - }() level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) // Parse the input in both parsers to look for disagreement and incompatible // inputs. @@ -208,7 +163,6 @@ func FallbackMatchersParser(l log.Logger, m *Metrics) ParseMatchers { } // The input is valid in the pkg/labels parser, but not the matchers/parse // parser. This means the input is not forwards compatible. - m.IncompatibleTotal.With(lbs).Inc() var sb strings.Builder for i, n := range cMatchers { sb.WriteString(n.String()) @@ -226,7 +180,6 @@ func FallbackMatchersParser(l log.Logger, m *Metrics) ParseMatchers { // then there is disagreement. We need to compare to labels.Matchers(cMatchers) // as cMatchers is a []*labels.Matcher not labels.Matchers. if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatchers, labels.Matchers(cMatchers)) { - m.DisagreeTotal.With(lbs).Inc() level.Warn(l).Log("msg", "Matchers input has disagreement", "input", input, "origin", origin) return cMatchers, nil } diff --git a/matchers/compat/parse_test.go b/matchers/compat/parse_test.go index d1490437bf..bb417ffbae 100644 --- a/matchers/compat/parse_test.go +++ b/matchers/compat/parse_test.go @@ -17,8 +17,6 @@ import ( "testing" "github.com/go-kit/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" @@ -27,51 +25,38 @@ import ( func TestFallbackMatcherParser(t *testing.T) { tests := []struct { - name string - input string - expected *labels.Matcher - err string - total float64 - disagreeTotal float64 - incompatibleTotal float64 - invalidTotal float64 + name string + input string + expected *labels.Matcher + err string }{{ name: "input is accepted", input: "foo=bar", expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - total: 1, }, { - name: "input is accepted in neither", - input: "foo!bar", - err: "bad matcher format: foo!bar", - total: 1, - invalidTotal: 1, + name: "input is accepted in neither", + input: "foo!bar", + err: "bad matcher format: foo!bar", }, { name: "input is accepted in matchers/parse but not pkg/labels", input: "foo🙂=bar", expected: mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), - total: 1, }, { - name: "input is accepted in pkg/labels but not matchers/parse", - input: "foo=!bar\\n", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "!bar\n"), - total: 1, - incompatibleTotal: 1, + name: "input is accepted in pkg/labels but not matchers/parse", + input: "foo=!bar\\n", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "!bar\n"), }, { // This input causes disagreement because \xf0\x9f\x99\x82 is the byte sequence for 🙂, // which is not understood by pkg/labels but is understood by matchers/parse. In such cases, // the fallback parser returns the result from pkg/labels. - name: "input causes disagreement", - input: "foo=\"\\xf0\\x9f\\x99\\x82\"", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), - total: 1, - disagreeTotal: 1, + name: "input causes disagreement", + input: "foo=\"\\xf0\\x9f\\x99\\x82\"", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), }} for _, test := range tests { t.Run(test.name, func(t *testing.T) { - m := NewMetrics(prometheus.NewRegistry()) - f := FallbackMatcherParser(log.NewNopLogger(), m) + f := FallbackMatcherParser(log.NewNopLogger()) matcher, err := f(test.input, "test") if test.err != "" { require.EqualError(t, err, test.err) @@ -79,24 +64,16 @@ func TestFallbackMatcherParser(t *testing.T) { require.NoError(t, err) require.EqualValues(t, test.expected, matcher) } - requireMetric(t, test.total, m.Total) - requireMetric(t, test.disagreeTotal, m.DisagreeTotal) - requireMetric(t, test.incompatibleTotal, m.IncompatibleTotal) - requireMetric(t, test.invalidTotal, m.InvalidTotal) }) } } func TestFallbackMatchersParser(t *testing.T) { tests := []struct { - name string - input string - expected labels.Matchers - err string - total float64 - disagreeTotal float64 - incompatibleTotal float64 - invalidTotal float64 + name string + input string + expected labels.Matchers + err string }{{ name: "input is accepted", input: "{foo=bar,bar=baz}", @@ -104,13 +81,10 @@ func TestFallbackMatchersParser(t *testing.T) { mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), mustNewMatcher(t, labels.MatchEqual, "bar", "baz"), }, - total: 1, }, { - name: "input is accepted in neither", - input: "{foo!bar}", - err: "bad matcher format: foo!bar", - total: 1, - invalidTotal: 1, + name: "input is accepted in neither", + input: "{foo!bar}", + err: "bad matcher format: foo!bar", }, { name: "input is accepted in matchers/parse but not pkg/labels", input: "{foo🙂=bar,bar=baz🙂}", @@ -118,7 +92,6 @@ func TestFallbackMatchersParser(t *testing.T) { mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), mustNewMatcher(t, labels.MatchEqual, "bar", "baz🙂"), }, - total: 1, }, { name: "is accepted in pkg/labels but not matchers/parse", input: "{foo=!bar,bar=$baz\\n}", @@ -126,8 +99,6 @@ func TestFallbackMatchersParser(t *testing.T) { mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), mustNewMatcher(t, labels.MatchEqual, "bar", "$baz\n"), }, - total: 1, - incompatibleTotal: 1, }, { // This input causes disagreement because \xf0\x9f\x99\x82 is the byte sequence for 🙂, // which is not understood by pkg/labels but is understood by matchers/parse. In such cases, @@ -137,14 +108,11 @@ func TestFallbackMatchersParser(t *testing.T) { expected: labels.Matchers{ mustNewMatcher(t, labels.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), }, - total: 1, - disagreeTotal: 1, }} for _, test := range tests { t.Run(test.name, func(t *testing.T) { - m := NewMetrics(prometheus.NewRegistry()) - f := FallbackMatchersParser(log.NewNopLogger(), m) + f := FallbackMatchersParser(log.NewNopLogger()) matchers, err := f(test.input, "test") if test.err != "" { require.EqualError(t, err, test.err) @@ -152,10 +120,6 @@ func TestFallbackMatchersParser(t *testing.T) { require.NoError(t, err) require.EqualValues(t, test.expected, matchers) } - requireMetric(t, test.total, m.Total) - requireMetric(t, test.disagreeTotal, m.DisagreeTotal) - requireMetric(t, test.incompatibleTotal, m.IncompatibleTotal) - requireMetric(t, test.invalidTotal, m.InvalidTotal) }) } } @@ -235,12 +199,3 @@ func TestIsValidUTF8LabelName(t *testing.T) { }) } } - -func requireMetric(t *testing.T, expected float64, m *prometheus.CounterVec) { - if expected == 0 { - require.Equal(t, 0, testutil.CollectAndCount(m)) - } else { - require.Equal(t, 1, testutil.CollectAndCount(m)) - require.Equal(t, expected, testutil.ToFloat64(m)) - } -} diff --git a/silence/silence_test.go b/silence/silence_test.go index da39b3a89a..2a880dd3b6 100644 --- a/silence/silence_test.go +++ b/silence/silence_test.go @@ -1334,12 +1334,12 @@ func TestValidateUTF8Matcher(t *testing.T) { // Change the mode to UTF-8 mode. ff, err := featurecontrol.NewFlags(log.NewNopLogger(), featurecontrol.FeatureUTF8StrictMode) require.NoError(t, err) - compat.InitFromFlags(log.NewNopLogger(), compat.RegisteredMetrics, ff) + compat.InitFromFlags(log.NewNopLogger(), ff) // Restore the mode to classic at the end of the test. ff, err = featurecontrol.NewFlags(log.NewNopLogger(), featurecontrol.FeatureClassicMode) require.NoError(t, err) - defer compat.InitFromFlags(log.NewNopLogger(), compat.RegisteredMetrics, ff) + defer compat.InitFromFlags(log.NewNopLogger(), ff) for _, c := range cases { checkErr(t, c.err, validateMatcher(c.m)) diff --git a/types/types_test.go b/types/types_test.go index 438f351535..ece6fb5c51 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -341,12 +341,12 @@ func TestValidateUTF8Ls(t *testing.T) { // Change the mode to UTF-8 mode. ff, err := featurecontrol.NewFlags(log.NewNopLogger(), featurecontrol.FeatureUTF8StrictMode) require.NoError(t, err) - compat.InitFromFlags(log.NewNopLogger(), compat.RegisteredMetrics, ff) + compat.InitFromFlags(log.NewNopLogger(), ff) // Restore the mode to classic at the end of the test. ff, err = featurecontrol.NewFlags(log.NewNopLogger(), featurecontrol.FeatureClassicMode) require.NoError(t, err) - defer compat.InitFromFlags(log.NewNopLogger(), compat.RegisteredMetrics, ff) + defer compat.InitFromFlags(log.NewNopLogger(), ff) for _, test := range tests { t.Run(test.name, func(t *testing.T) { From f00025d037d8c89af45f2aa42ebefeba2d21a9ac Mon Sep 17 00:00:00 2001 From: TJ Hoplock <33664289+tjhop@users.noreply.github.com> Date: Tue, 13 Feb 2024 06:17:24 -0500 Subject: [PATCH 11/36] feat: add counter to track alerts dropped outside of time_intervals (#3565) * feat: add counter to track alerts dropped outside of time_intervals Addresses: #3512 This adds a new counter metric `alertmanager_alerts_supressed_total` that is incremented by `len(alerts)` when an alert is suppressed for being outside of a time_interval, ie inside of a mute_time_intervals or outside of an active_time_intervals. Signed-off-by: TJ Hoplock * test: add time interval suppression metric checks for notify Signed-off-by: TJ Hoplock * test: fix failure message log values in notifier Signed-off-by: TJ Hoplock * ref: address PR feedback for #3565 Signed-off-by: TJ Hoplock * fix: track suppressed notifications metric for inhibit/silence Based on PR feedback: https://github.com/prometheus/alertmanager/pull/3565/files#r1393068026 Signed-off-by: TJ Hoplock * fix: broken notifier tests - fixed metric count check to properly check the diff between input/output notifications from the suppression to compare to suppression metric, was previously inverted to compare to how many notifications it suppressed. - stopped using `Reset()` to compare collection counts between the multiple stages that are executed in `TestMuteStageWithSilences()`. the intent was to compare a clean metric collection after each stage execution, but the final stage where all silences are lifted results in no metric being created in the test, causing `prom_testutil.ToFloat64()` to panic. changed to separate vars to check counts between each stage, with care to consider prior counts. Signed-off-by: TJ Hoplock * rename metric and add constants Signed-off-by: gotjosh --------- Signed-off-by: TJ Hoplock Signed-off-by: gotjosh Co-authored-by: gotjosh --- notify/notify.go | 58 ++++++++++++++++++++++++++++++++----------- notify/notify_test.go | 45 +++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/notify/notify.go b/notify/notify.go index 0a2b0d032b..30861a3027 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -251,6 +251,7 @@ type Metrics struct { numTotalFailedNotifications *prometheus.CounterVec numNotificationRequestsTotal *prometheus.CounterVec numNotificationRequestsFailedTotal *prometheus.CounterVec + numNotificationSuppressedTotal *prometheus.CounterVec notificationLatencySeconds *prometheus.HistogramVec ff featurecontrol.Flagger @@ -284,6 +285,11 @@ func NewMetrics(r prometheus.Registerer, ff featurecontrol.Flagger) *Metrics { Name: "notification_requests_failed_total", Help: "The total number of failed notification requests.", }, labels), + numNotificationSuppressedTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "alertmanager", + Name: "notifications_suppressed_total", + Help: "The total number of notifications suppressed for being outside of active time intervals or within muted time intervals.", + }, []string{"reason"}), notificationLatencySeconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: "alertmanager", Name: "notification_latency_seconds", @@ -296,7 +302,7 @@ func NewMetrics(r prometheus.Registerer, ff featurecontrol.Flagger) *Metrics { r.MustRegister( m.numNotifications, m.numTotalFailedNotifications, m.numNotificationRequestsTotal, m.numNotificationRequestsFailedTotal, - m.notificationLatencySeconds, + m.numNotificationSuppressedTotal, m.notificationLatencySeconds, ) return m @@ -381,10 +387,10 @@ func (pb *PipelineBuilder) New( rs := make(RoutingStage, len(receivers)) ms := NewGossipSettleStage(peer) - is := NewMuteStage(inhibitor) - tas := NewTimeActiveStage(intervener) - tms := NewTimeMuteStage(intervener) - ss := NewMuteStage(silencer) + is := NewMuteStage(inhibitor, pb.metrics) + tas := NewTimeActiveStage(intervener, pb.metrics) + tms := NewTimeMuteStage(intervener, pb.metrics) + ss := NewMuteStage(silencer, pb.metrics) for name := range receivers { st := createReceiverStage(name, receivers[name], wait, notificationLog, pb.metrics) @@ -507,14 +513,22 @@ func (n *GossipSettleStage) Exec(ctx context.Context, _ log.Logger, alerts ...*t return ctx, alerts, nil } +const ( + suppressedReasonSilence = "silence" + suppressedReasonInhibition = "inhibition" + suppressedReasonMuteTimeInterval = "mute_time_interval" + suppressedReasonActiveTimeInterval = "active_time_interval" +) + // MuteStage filters alerts through a Muter. type MuteStage struct { - muter types.Muter + muter types.Muter + metrics *Metrics } // NewMuteStage return a new MuteStage. -func NewMuteStage(m types.Muter) *MuteStage { - return &MuteStage{muter: m} +func NewMuteStage(m types.Muter, metrics *Metrics) *MuteStage { + return &MuteStage{muter: m, metrics: metrics} } // Exec implements the Stage interface. @@ -535,7 +549,18 @@ func (n *MuteStage) Exec(ctx context.Context, logger log.Logger, alerts ...*type } if len(muted) > 0 { level.Debug(logger).Log("msg", "Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted)) + + var reason string + switch n.muter.(type) { + case *silence.Silencer: + reason = suppressedReasonSilence + case *inhibit.Inhibitor: + reason = suppressedReasonInhibition + default: + } + n.metrics.numNotificationSuppressedTotal.WithLabelValues(reason).Add(float64(len(muted))) } + return ctx, filtered, nil } @@ -894,13 +919,14 @@ func (n SetNotifiesStage) Exec(ctx context.Context, l log.Logger, alerts ...*typ } type timeStage struct { - muter types.TimeMuter + muter types.TimeMuter + metrics *Metrics } type TimeMuteStage timeStage -func NewTimeMuteStage(m types.TimeMuter) *TimeMuteStage { - return &TimeMuteStage{m} +func NewTimeMuteStage(m types.TimeMuter, metrics *Metrics) *TimeMuteStage { + return &TimeMuteStage{m, metrics} } // Exec implements the stage interface for TimeMuteStage. @@ -927,7 +953,8 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type // If the current time is inside a mute time, all alerts are removed from the pipeline. if muted { - level.Debug(l).Log("msg", "Notifications not sent, route is within mute time") + tms.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonMuteTimeInterval).Add(float64(len(alerts))) + level.Debug(l).Log("msg", "Notifications not sent, route is within mute time", "alerts", len(alerts)) return ctx, nil, nil } return ctx, alerts, nil @@ -935,8 +962,8 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type type TimeActiveStage timeStage -func NewTimeActiveStage(m types.TimeMuter) *TimeActiveStage { - return &TimeActiveStage{m} +func NewTimeActiveStage(m types.TimeMuter, metrics *Metrics) *TimeActiveStage { + return &TimeActiveStage{m, metrics} } // Exec implements the stage interface for TimeActiveStage. @@ -964,7 +991,8 @@ func (tas TimeActiveStage) Exec(ctx context.Context, l log.Logger, alerts ...*ty // If the current time is not inside an active time, all alerts are removed from the pipeline if !muted { - level.Debug(l).Log("msg", "Notifications not sent, route is not within active time") + tas.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonActiveTimeInterval).Add(float64(len(alerts))) + level.Debug(l).Log("msg", "Notifications not sent, route is not within active time", "alerts", len(alerts)) return ctx, nil, nil } diff --git a/notify/notify_test.go b/notify/notify_test.go index 2ae452d48b..d5ce9612e3 100644 --- a/notify/notify_test.go +++ b/notify/notify_test.go @@ -666,7 +666,8 @@ func TestMuteStage(t *testing.T) { return ok }) - stage := NewMuteStage(muter) + metrics := NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}) + stage := NewMuteStage(muter, metrics) in := []model.LabelSet{ {}, @@ -705,6 +706,10 @@ func TestMuteStage(t *testing.T) { if !reflect.DeepEqual(got, out) { t.Fatalf("Muting failed, expected: %v\ngot %v", out, got) } + suppressed := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(in) - len(got)) != suppressed { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(in) - len(got)), suppressed) + } } func TestMuteStageWithSilences(t *testing.T) { @@ -720,9 +725,11 @@ func TestMuteStageWithSilences(t *testing.T) { t.Fatal(err) } - marker := types.NewMarker(prometheus.NewRegistry()) + reg := prometheus.NewRegistry() + marker := types.NewMarker(reg) silencer := silence.NewSilencer(silences, marker, log.NewNopLogger()) - stage := NewMuteStage(silencer) + metrics := NewMetrics(reg, featurecontrol.NoopFlags{}) + stage := NewMuteStage(silencer, metrics) in := []model.LabelSet{ {}, @@ -765,6 +772,10 @@ func TestMuteStageWithSilences(t *testing.T) { if !reflect.DeepEqual(got, out) { t.Fatalf("Muting failed, expected: %v\ngot %v", out, got) } + suppressedRoundOne := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(in) - len(got)) != suppressedRoundOne { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(in) - len(got)), suppressedRoundOne) + } // Do it again to exercise the version tracking of silences. _, alerts, err = stage.Exec(context.Background(), log.NewNopLogger(), inAlerts...) @@ -781,6 +792,11 @@ func TestMuteStageWithSilences(t *testing.T) { t.Fatalf("Muting failed, expected: %v\ngot %v", out, got) } + suppressedRoundTwo := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(in) - len(got) + suppressedRoundOne) != suppressedRoundTwo { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(in) - len(got)), suppressedRoundTwo) + } + // Expire the silence and verify that no alerts are silenced now. if err := silences.Expire(silID); err != nil { t.Fatal(err) @@ -798,6 +814,10 @@ func TestMuteStageWithSilences(t *testing.T) { if !reflect.DeepEqual(got, in) { t.Fatalf("Unmuting failed, expected: %v\ngot %v", in, got) } + suppressedRoundThree := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(in) - len(got) + suppressedRoundTwo) != suppressedRoundThree { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(in) - len(got)), suppressedRoundThree) + } } func TestTimeMuteStage(t *testing.T) { @@ -874,7 +894,8 @@ func TestTimeMuteStage(t *testing.T) { } m := map[string][]timeinterval.TimeInterval{"test": intervals} intervener := timeinterval.NewIntervener(m) - stage := NewTimeMuteStage(intervener) + metrics := NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}) + stage := NewTimeMuteStage(intervener, metrics) outAlerts := []*types.Alert{} nonMuteCount := 0 @@ -908,6 +929,10 @@ func TestTimeMuteStage(t *testing.T) { if len(outAlerts) != nonMuteCount { t.Fatalf("Expected %d alerts after time mute stage but got %d", nonMuteCount, len(outAlerts)) } + suppressed := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(cases) - nonMuteCount) != suppressed { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(cases) - nonMuteCount), suppressed) + } } func TestTimeActiveStage(t *testing.T) { @@ -933,6 +958,11 @@ func TestTimeActiveStage(t *testing.T) { labels: model.LabelSet{"mute": "me"}, shouldMute: true, }, + { + fireTime: "02 Dec 20 16:59 +0000", + labels: model.LabelSet{"mute": "me"}, + shouldMute: true, + }, { // Tuesday before 5pm fireTime: "01 Dec 20 16:59 +0000", @@ -959,7 +989,8 @@ func TestTimeActiveStage(t *testing.T) { } m := map[string][]timeinterval.TimeInterval{"test": intervals} intervener := timeinterval.NewIntervener(m) - stage := NewTimeActiveStage(intervener) + metrics := NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}) + stage := NewTimeActiveStage(intervener, metrics) outAlerts := []*types.Alert{} nonMuteCount := 0 @@ -993,6 +1024,10 @@ func TestTimeActiveStage(t *testing.T) { if len(outAlerts) != nonMuteCount { t.Fatalf("Expected %d alerts after time mute stage but got %d", nonMuteCount, len(outAlerts)) } + suppressed := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal)) + if (len(cases) - nonMuteCount) != suppressed { + t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(cases) - nonMuteCount), suppressed) + } } func BenchmarkHashAlert(b *testing.B) { From 44d5f70c3b894a08096f2a4b75c737892acfba49 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 13 Feb 2024 11:37:42 +0000 Subject: [PATCH 12/36] Fix a small number of inconsistencies in compat package logging (#3718) This commit fixes a small number of inconsistencies in the compat package logging. It now has consistent use of classic matchers parser and UTF-8 matchers parser, instead of old matchers parser and new matchers parser. Signed-off-by: George Robinson --- matchers/compat/parse.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matchers/compat/parse.go b/matchers/compat/parse.go index 7aa4e2d95b..0c0dfffb1f 100644 --- a/matchers/compat/parse.go +++ b/matchers/compat/parse.go @@ -133,7 +133,7 @@ func FallbackMatcherParser(l log.Logger) ParseMatcher { // The input is valid in the pkg/labels parser, but not the matchers/parse // parser. This means the input is not forwards compatible. suggestion := cMatcher.String() - level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the old matchers parser as a fallback. To make this input compatible with the new parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) + level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatcher, nil } // If the input is valid in both parsers, but produces different results, @@ -173,7 +173,7 @@ func FallbackMatchersParser(l log.Logger) ParseMatchers { suggestion := sb.String() // The input is valid in the pkg/labels parser, but not the // new matchers/parse parser. - level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the old matchers parser as a fallback. To make this input compatible with the new parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) + level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatchers, nil } // If the input is valid in both parsers, but produces different results, From 2fd1b52c988cec3814413ee493e9c1bdb2a1cb40 Mon Sep 17 00:00:00 2001 From: Lucas Burigo Date: Tue, 13 Feb 2024 13:40:46 +0100 Subject: [PATCH 13/36] Update notification_examples.md (#3707) Signed-off-by: Lucas Burigo --- docs/notification_examples.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/notification_examples.md b/docs/notification_examples.md index beb0a27c4d..85ac6e9992 100644 --- a/docs/notification_examples.md +++ b/docs/notification_examples.md @@ -76,7 +76,7 @@ Receiver ## Defining reusable templates Going back to our first example, we can also provide a file containing named templates which are then loaded by Alertmanager in order to avoid complex templates that span many lines. -Create a file under `/alertmanager/template/myorg.tmpl` and create a template in it named "slack.myorg.txt": +Create a file under `/alertmanager/template/myorg.tmpl` and create a template in it named "slack.myorg.text": ``` {{ define "slack.myorg.text" }}https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}{{ end}} @@ -102,4 +102,4 @@ templates: - '/etc/alertmanager/templates/myorg.tmpl' ``` -This example is explained in further detail in this [blogpost](https://prometheus.io/blog/2016/03/03/custom-alertmanager-templates/). \ No newline at end of file +This example is explained in further detail in this [blogpost](https://prometheus.io/blog/2016/03/03/custom-alertmanager-templates/). From 604d442f307ede363708573fb3bddec9f4c5ff00 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 13 Feb 2024 14:35:03 +0000 Subject: [PATCH 14/36] Fix log line in featurecontrol (#3719) This commit fixes a log line in the featurecontrol package which should be "UTF-8 strict mode" and not "UTF-8 mode". Signed-off-by: George Robinson --- featurecontrol/featurecontrol.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/featurecontrol/featurecontrol.go b/featurecontrol/featurecontrol.go index a8a5585267..9ff7a2d8fd 100644 --- a/featurecontrol/featurecontrol.go +++ b/featurecontrol/featurecontrol.go @@ -97,7 +97,7 @@ func NewFlags(logger log.Logger, features string) (Flagger, error) { level.Warn(logger).Log("msg", "Classic mode enabled") case FeatureUTF8StrictMode: opts = append(opts, enableUTF8StrictMode()) - level.Warn(logger).Log("msg", "UTF-8 mode enabled") + level.Warn(logger).Log("msg", "UTF-8 strict mode enabled") default: return nil, fmt.Errorf("Unknown option '%s' for --enable-feature", feature) } From 4d6ddd25c931485b803b2a4df2e9f878b7536abb Mon Sep 17 00:00:00 2001 From: George Robinson Date: Tue, 13 Feb 2024 15:38:44 +0000 Subject: [PATCH 15/36] Fix panic in acceptance tests (#3592) * Fix panic in acceptance tests This commit attempts to address a panic that occurs in acceptance tests if a server in the cluster fails to start. Signed-off-by: George Robinson * Remove started and check am.cmd.Process != nil Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- test/with_api_v2/acceptance.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/with_api_v2/acceptance.go b/test/with_api_v2/acceptance.go index a6c4abd874..de59ab8581 100644 --- a/test/with_api_v2/acceptance.go +++ b/test/with_api_v2/acceptance.go @@ -169,17 +169,15 @@ func (t *AcceptanceTest) Run() { for _, am := range t.amc.ams { am.errc = errc - defer func(am *Alertmanager) { - am.Terminate() - am.cleanup() - t.Logf("stdout:\n%v", am.cmd.Stdout) - t.Logf("stderr:\n%v", am.cmd.Stderr) - }(am) + t.T.Cleanup(am.Terminate) + t.T.Cleanup(am.cleanup) } err := t.amc.Start() if err != nil { - t.T.Fatal(err) + t.T.Log(err) + t.T.Fail() + return } // Set the reference time right before running the test actions to avoid @@ -251,10 +249,10 @@ type Alertmanager struct { apiAddr string clusterAddr string clientV2 *apiclient.AlertmanagerAPI - cmd *exec.Cmd confFile *os.File dir string + cmd *exec.Cmd errc chan<- error } @@ -386,8 +384,12 @@ func (amc *AlertmanagerCluster) Terminate() { // data. func (am *Alertmanager) Terminate() { am.t.Helper() - if err := syscall.Kill(am.cmd.Process.Pid, syscall.SIGTERM); err != nil { - am.t.Logf("Error sending SIGTERM to Alertmanager process: %v", err) + if am.cmd.Process != nil { + if err := syscall.Kill(am.cmd.Process.Pid, syscall.SIGTERM); err != nil { + am.t.Logf("Error sending SIGTERM to Alertmanager process: %v", err) + } + am.t.Logf("stdout:\n%v", am.cmd.Stdout) + am.t.Logf("stderr:\n%v", am.cmd.Stderr) } } @@ -401,8 +403,10 @@ func (amc *AlertmanagerCluster) Reload() { // Reload sends the reloading signal to the Alertmanager process. func (am *Alertmanager) Reload() { am.t.Helper() - if err := syscall.Kill(am.cmd.Process.Pid, syscall.SIGHUP); err != nil { - am.t.Fatalf("Error sending SIGHUP to Alertmanager process: %v", err) + if am.cmd.Process != nil { + if err := syscall.Kill(am.cmd.Process.Pid, syscall.SIGHUP); err != nil { + am.t.Fatalf("Error sending SIGHUP to Alertmanager process: %v", err) + } } } From c2cf3db0457762e44bdabf296691a133d5ace5a2 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Wed, 14 Feb 2024 09:20:42 +0000 Subject: [PATCH 16/36] Support UTF-8 label matchers: Update the docs on how to use UTF-8 in label matchers and parse mode feature flags (#3572) * Update the docs on how to use UTF-8 in label matchers and parse mode feature flags Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- docs/configuration.md | 212 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 22 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index f5d911b7c0..b4b421d0d0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -421,27 +421,146 @@ source_matchers: ## Label matchers -Label matchers are used both in routes and inhibition rules to match certain alerts. +Label matchers match alerts to routes, silences, and inhibition rules. + +**Important**: Prometheus is adding support for UTF-8 in labels and metrics. In order to also support UTF-8 in the Alertmanager, Alertmanager versions 0.27 and later have a new parser for matchers that has a number of backwards incompatible changes. While most matchers will be forward-compatible, some will not. Alertmanager is operating a transition period where it supports both UTF-8 and classic matchers, and has provided a number of tools to help you prepare for the transition. + +If this is a new Alertmanager installation, we recommend enabling UTF-8 strict mode before creating an Alertmanager configuration file. You can find instructions on how to enable UTF-8 strict mode [here](#utf-8-strict-mode). + +If this is an existing Alertmanager installation, we recommend running the Alertmanager in the default mode called fallback mode before enabling UTF-8 strict mode. In this mode, Alertmanager will log a warning if you need to make any changes to your configuration file before UTF-8 strict mode can be enabled. Alertmanager will make UTF-8 strict mode the default in the next two versions, so it's important to transition as soon as possible. + +Irrespective of whether an Alertmanager installation is a new or existing installation, you can also use `amtool` to validate that an Alertmanager configuration file is compatible with UTF-8 strict mode before enabling it in Alertmanager server. You do not need a running Alertmanager server to do this. You can find instructions on how to validate an Alertmanager configuration file using `amtool` [here](#verification). + +### Alertmanager server operational modes + +During the transition period, Alertmanager supports three modes of operation. These are known as fallback mode, UTF-8 strict mode and classic mode. Fallback mode is the default mode. + +Operators of Alertmanager servers should transition to UTF-8 strict mode before the end of the transition period. Alertmanager will make UTF-8 strict mode the default in the next two versions, so it's important to transition as soon as possible. + +#### Fallback mode + +Alertmanager runs in a special mode called fallback mode as its default mode. As operators, you should not experience any difference in how your routes, silences or inhibition rules work. + +In fallback mode, configurations are first parsed as UTF-8 matchers, and if incompatible with the UTF-8 parser, are then parsed as classic matchers. If your Alertmanager configuration contains matchers that are incompatible with the UTF-8 parser, Alertmanager will parse them as classic matchers and log a warning. This warning also includes a suggestion on how to change the matchers from classic matchers to UTF-8 matchers. For example: + +> ts=2024-02-11T10:00:00Z caller=parse.go:176 level=warn msg="Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue." input="foo=" origin=config err="end of input: expected label value" suggestion="foo=\"\"" + +Here the matcher `foo=` can be made into a valid UTF-8 matcher by double quoting the right hand side of the expression to give `foo=""`. These two matchers are equivalent, however with UTF-8 matchers the right hand side of the matcher is a required field. + +In rare cases, a configuration can cause disagreement between the UTF-8 and classic parser. This happens when a matcher is valid in both parsers, but due to added support for UTF-8, results in different parsings depending on which parser is used. If your Alertmanager configuration has disagreement, Alertmanager will use the classic parser and log a warning. For example: + +> ts=2024-02-11T10:00:00Z caller=parse.go:183 level=warn msg="Matchers input has disagreement" input="qux=\"\\xf0\\x9f\\x99\\x82\"\n" origin=config + +Any occurrences of disagreement should be looked at on a case by case basis as depending on the nature of the disagreement, the configuration might not need updating before enabling UTF-8 strict mode. For example `\xf0\x9f\x99\x82` is the byte sequence for the 🙂 emoji. If the intention is to match a literal 🙂 emoji then no change is required. However, if the intention is to match the literal `\xf0\x9f\x99\x82` then the matcher should be changed to `qux="\\xf0\\x9f\\x99\\x82"`. + +#### UTF-8 strict mode + +In UTF-8 strict mode, Alertmanager disables support for classic matchers: + +> alertmanager --config.file=config.yml --enable-feature="utf8-strict-mode" + +This mode should be enabled for new Alertmanager installations, and existing Alertmanager installations once all warnings of incompatible matchers have been resolved. Alertmanager will not start in UTF-8 strict mode until all the warnings of incompatible matchers have been resolved: + +> ts=2024-02-11T10:00:00Z caller=coordinator.go:118 level=error component=configuration msg="Loading configuration file failed" file=config.yml err="end of input: expected label value" + +UTF-8 strict mode will be the default mode of Alertmanager at the end of the transition period. + +#### Classic mode + +Classic mode is equivalent to Alertmanager versions 0.26.0 and older: + +``` +alertmanager --config.file=config.yml --enable-feature="classic-mode" +``` + +You can use this mode if you suspect there is an issue with fallback mode or UTF-8 strict mode. In such cases, please open an issue on GitHub with as much information as possible. + +### Verification + +You can use `amtool` to validate that an Alertmanager configuration file is compatible with UTF-8 strict mode before enabling it in Alertmanager server. You do not need a running Alertmanager server to do this. + +Just like Alertmanager server, `amtool` will log a warning if the configuration is incompatible or contains disagreement: + +``` +amtool check-config config.yml +Checking 'config.yml' +level=warn msg="Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue." input="foo=" origin=config err="end of input: expected label value" suggestion="foo=\"\"" +level=warn msg="Matchers input has disagreement" input="qux=\"\\xf0\\x9f\\x99\\x82\"\n" origin=config + SUCCESS +Found: + - global config + - route + - 2 inhibit rules + - 2 receivers + - 0 templates +``` + +You will know if a configuration is compatible with UTF-8 strict mode when no warnings are logged in `amtool`: + +``` +amtool check-config config.yml +Checking 'config.yml' SUCCESS +Found: + - global config + - route + - 2 inhibit rules + - 2 receivers + - 0 templates +``` + +You can also use `amtool` in UTF-8 strict mode as an additional level of verification. You will know that a configuration is invalid because the command will fail: + +``` +amtool check-config config.yml --enable-feature="utf8-strict-mode" +level=warn msg="UTF-8 mode enabled" +Checking 'config.yml' FAILED: end of input: expected label value + +amtool: error: failed to validate 1 file(s) +``` + +You will know that a configuration is valid because the command will succeed: + +``` +amtool check-config config.yml --enable-feature="utf8-strict-mode" +level=warn msg="UTF-8 mode enabled" +Checking 'config.yml' SUCCESS +Found: + - global config + - route + - 2 inhibit rules + - 2 receivers + - 0 templates +``` ### `` -A matcher is a string with a syntax inspired by PromQL and OpenMetrics. The syntax of a matcher consists of three tokens: +#### UTF-8 matchers -- A valid Prometheus label name. +A UTF-8 matcher consists of three tokens: + +- An unquoted literal or a double-quoted string for the label name. +- One of `=`, `!=`, `=~`, or `!~`. `=` means equals, `!=` means not equal, `=~` means matches the regular expression and `!~` means doesn't match the regular expression. +- An unquoted literal or a double-quoted string for the regular expression or label value. + +Unquoted literals can contain all UTF-8 characters other than the reserved characters. These are whitespace, and all characters in ``` { } ! = ~ , \ " ' ` ```. For example, `foo`, `[a-zA-Z]+`, and `Προμηθεύς` (Prometheus in Greek) are all examples of valid unquoted literals. However, `foo!` is not a valid literal as `!` is a reserved character. + +Double-quoted strings can contain all UTF-8 characters. Unlike unquoted literals, there are no reserved characters. You can even use UTF-8 code points. For example, `"foo!"`, `"bar,baz"`, `"\"baz qux\""` and `"\xf0\x9f\x99\x82"` are valid double-quoted strings. + +#### Classic matchers -- One of `=`, `!=`, `=~`, or `!~`. `=` means equals, `!=` means that the strings are not equal, `=~` is used for equality of regex expressions and `!~` is used for un-equality of regex expressions. They have the same meaning as known from PromQL selectors. +A classic matcher is a string with a syntax inspired by PromQL and OpenMetrics. The syntax of a classic matcher consists of three tokens: +- A valid Prometheus label name. +- One of `=`, `!=`, `=~`, or `!~`. `=` means equals, `!=` means that the strings are not equal, `=~` is used for equality of regex expressions and `!~` is used for un-equality of regex expressions. They have the same meaning as known from PromQL selectors. - A UTF-8 string, which may be enclosed in double quotes. Before or after each token, there may be any amount of whitespace. The 3rd token may be the empty string. Within the 3rd token, OpenMetrics escaping rules apply: `\"` for a double-quote, `\n` for a line feed, `\\` for a literal backslash. Unescaped `"` must not occur inside the 3rd token (only as the 1st or last character). However, literal line feed characters are tolerated, as are single `\` characters not followed by `\`, `n`, or `"`. They act as a literal backslash in that case. -Matchers are ANDed together, meaning that all matchers must evaluate to "true" when tested against the labels on a given alert. For example, an alert with these labels: +#### Composition of matchers -```json -{"alertname":"Watchdog","severity":"none"} -``` +You can compose matchers to create complex match expressions. When composed, all matchers must match for the entire expression to match. For example, the expression `{alertname="Watchdog", severity=~"warning|critical"}` will match an alert with labels `alertname=Watchdog, severity=critical` but not an alert with labels `alertname=Watchdog, severity=none` as while the alertname is Watchdog the severity is neither warning nor critical. -would NOT match this list of matchers: +You can compose matchers into expressions with a YAML list: ```yaml matchers: @@ -449,37 +568,86 @@ matchers: - severity =~ "warning|critical" ``` -In the configuration, multiple matchers are combined in a YAML list. However, it is also possible to combine multiple matchers within a single YAML string, again using syntax inspired by PromQL. In such a string, a leading `{` and/or a trailing `}` is optional and will be trimmed before further parsing. Individual matchers are separated by commas outside of quoted parts of the string. Those commas may be surrounded by whitespace. Parts of the string inside unescaped double quotes `"…"` are considered quoted (and commas don't act as separators there). If double quotes are escaped with a single backslash `\`, they are ignored for the purpose of identifying quoted parts of the input string. If the input string, after trimming the optional trailing `}`, ends with a comma, followed by optional whitespace, this comma and whitespace will be trimmed. +or as a PromQL inspired expression where each matcher is comma separated: + +``` +{alertname="Watchdog", severity=~"warning|critical"} +``` + +A single trailing comma is permitted: + +``` +{alertname="Watchdog", severity=~"warning|critical",} +``` + +The open `{` and close `}` brace are optional: + +``` +alertname="Watchdog", severity=~"warning|critical" +``` + +However, both must be either present or omitted. You cannot have incomplete open or close braces: + +``` +{alertname="Watchdog", severity=~"warning|critical" +``` + +``` +alertname="Watchdog", severity=~"warning|critical"} +``` + +You cannot have duplicate open or close braces either: + +``` +{{alertname="Watchdog", severity=~"warning|critical",}} +``` + +Whitespace (spaces, tabs and newlines) is permitted outside double quotes and has no effect on the matchers themselves. For example: + +``` +{ + alertname = "Watchdog", + severity =~ "warning|critical", +} +``` + +is equivalent to: -Here are some examples of valid string matchers: +``` +{alertname="Watchdog",severity=~"warning|critical"} +``` -1. Shown below are two equality matchers combined in a long form YAML list. +#### More examples + +Here are some more examples: + +1. Two equals matchers composed as a YAML list: ```yaml matchers: - foo = bar - - dings !=bums + - dings != bums ``` -2. Similar to example 1, shown below are two equality matchers combined in a short form YAML list. +2. Two matchers combined composed as a short-form YAML list: ```yaml matchers: [ foo = bar, dings != bums ] ``` - As shown below, in the short-form, it's generally better to quote the list elements to avoid problems with special characters like commas: - - ```yaml - matchers: [ "foo = \"bar,baz\"", "dings != bums" ] - ``` + As shown below, in the short-form, it's better to use double quotes to avoid problems with special characters like commas: + + ```yaml + matchers: [ "foo = \"bar,baz\"", "dings != bums" ] + ``` -3. You can also put both matchers into one PromQL-like string. Single quotes for the whole string work best here. +3. You can also put both matchers into one PromQL-like string. Single quotes work best here: ```yaml - matchers: [ '{foo="bar",dings!="bums"}' ] + matchers: [ '{foo="bar", dings!="bums"}' ] ``` -4. To avoid any confusion about YAML string quoting and escaping, you can use YAML block quoting and then only worry about the OpenMetrics escaping inside the block. A complex example with a regular expression and different quotes inside the label value is shown below: +4. To avoid issues with escaping and quoting rules in YAML, you can also use a YAML block: ```yaml matchers: From d352d16e277a32714bff75af0068b79fc8cf8a0d Mon Sep 17 00:00:00 2001 From: gotjosh Date: Wed, 14 Feb 2024 11:18:28 +0000 Subject: [PATCH 17/36] Fix flaky test TestClusterJoinAndReconnect/TestTLSConnection (#3722) wait until `p2.Status()` returns because it blocks until we're ready - that way, we're guaranteed to know that the cluster size is 2. Signed-off-by: gotjosh --- cluster/cluster_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go index 768608758c..53db6d0895 100644 --- a/cluster/cluster_test.go +++ b/cluster/cluster_test.go @@ -335,6 +335,8 @@ func testTLSConnection(t *testing.T) { require.NoError(t, err) go p2.Settle(context.Background(), 0*time.Second) p2.WaitReady(context.Background()) + require.Equal(t, "ready", p2.Status()) + require.Equal(t, 2, p1.ClusterSize()) p2.Leave(0 * time.Second) require.Equal(t, 1, p1.ClusterSize()) From b7d4c4ab867400102e5e9b97a26982f6f8f94b96 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Wed, 14 Feb 2024 11:27:10 +0000 Subject: [PATCH 18/36] Release: Cut 0.27.0-rc.0 (#3721) * Release: Cut 0.27.0-rc.0 Signed-off-by: gotjosh * small fixes - typo in respond - add PR numbers for UTF-8 Signed-off-by: gotjosh * more worthsmithing Signed-off-by: gotjosh * Fix flaky test TestClusterJoinAndReconnect/TestTLSConnection (#3722) wait until `p2.Status()` returns because it blocks until we're ready - that way, we're guaranteed to know that the cluster size is 2. Signed-off-by: gotjosh --------- Signed-off-by: gotjosh --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d9a2a60b..2db2d56a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +## 0.27.0-rc.0 / 2024-02-14 + +* [CHANGE] Discord Integration: Enforce max length in `message`. #3597 +* [CHANGE] API: Removal of all `api/v1/` endpoints. These endpoints now log and return a deprecation message and respond with a status code of `410`. #2970 +* [FEATURE] UTF-8 Support: Introduction of support for any UTF-8 character as part of label names and matchers. Please read more below. #3453, #3483, #3567, #3570 +* [FEATURE] Metrics: Introduced the experimental feature flag `--enable-feature=receiver-name-in-metrics` to include the receiver name in the following metrics: #3045 + * `alertmanager_notifications_total` + * `alertmanager_notifications_failed_totall` + * `alertmanager_notification_requests_total` + * `alertmanager_notification_requests_failed_total` + * `alertmanager_notification_latency_seconds` +* [FEATURE] Metrics: Introduced a new gauge named `alertmanager_inhibition_rules` that counts the number of configured inhibition rules. #3681 +* [FEATURE] Metrics: Introduced a new counter named `alertmanager_alerts_supressed_total` that tracks muted alerts, it contains a `reason` label to indicate the source of the mute. #3565 +* [ENHANCEMENT] Discord Integration: Introduced support for `webhook_url_file`. #3555 +* [ENHANCEMENT] Microsoft Teams Integration: Introduced support for `webhook_url_file`. #3555 +* [ENHANCEMENT] Microsoft Teams Integration: Add support for `summary`. #3616 +* [ENHANCEMENT] Metrics: Notification metrics now support two new values for the label `reason`, `contextCanceled` and `contextDeadlineExceeded`. #3631 +* [ENHANCEMENT] Email Integration: Contents of `auth_password_file` are now trimmed of prefixed and suffixed whitespace. #3680 +* [BUGFIX] amtool: Fixes the error `scheme required for webhook url` when using amtool with `--alertmanager.url`. #3509 +* [BUGFIX] Mixin: Fix `AlertmanagerFailedToSendAlerts`, `AlertmanagerClusterFailedToSendAlerts`, and `AlertmanagerClusterFailedToSendAlerts` to make sure they ignore the `reason` label. #3599 + +### Removal of API v1 + +The Alertmanager `v1` API has been deprecated since January 2019 with the release of Alertmanager `v0.16.0`. With the release of version `0.27.0` it is now removed. +A successful HTTP request to any of the `v1` endpoints will log and return a deprecation message while responding with a status code of `410`. +Please ensure you switch to the `v2` equivalent endpoint in your integrations before upgrading. + +### Alertmanager support for all UTF-8 characters in matchers and label names + +Starting with Alertmanager `v0.27.0`, we have a new parser for matchers that has a number of backwards incompatible changes. While most matchers will be forward-compatible, some will not. Alertmanager is operating a transition period where it supports both UTF-8 and classic matchers, so **it's entirely safe to upgrade without any additional configuration**. With that said, we recommend the following: + +- If this is a new Alertmanager installation, we recommend enabling UTF-8 strict mode before creating an Alertmanager configuration file. You can enable strict mode with `alertmanager --config.file=config.yml --enable-feature="utf8-strict-mode"`. + +- If this is an existing Alertmanager installation, we recommend running the Alertmanager in the default mode called fallback mode before enabling UTF-8 strict mode. In this mode, Alertmanager will log a warning if you need to make any changes to your configuration file before UTF-8 strict mode can be enabled. **Alertmanager will make UTF-8 strict mode the default in the next two versions**, so it's important to transition as soon as possible. + +Irrespective of whether an Alertmanager installation is a new or existing installation, you can also use `amtool` to validate that an Alertmanager configuration file is compatible with UTF-8 strict mode before enabling it in Alertmanager server by running `amtool check-config config.yml` and inspecting the log messages. + +Should you encounter any problems, you can run the Alertmanager with just the classic parser enabled by running `alertmanager --config.file=config.yml --enable-feature="classic-mode"`. If so, please submit a bug report via GitHub issues. + ## 0.26.0 / 2023-08-23 * [SECURITY] Fix stored XSS via the /api/v1/alerts endpoint in the Alertmanager UI. CVE-2023-40577 diff --git a/VERSION b/VERSION index 4e8f395fa5..205fccd4ff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.26.0 +0.27.0-rc.0 From 80b3cb072fbd9466541f91d520f1c61887981ede Mon Sep 17 00:00:00 2001 From: gotjosh Date: Thu, 15 Feb 2024 11:12:58 +0000 Subject: [PATCH 19/36] Notify: Several improvements around the `suppressed_total` metric and logs for muting. (#3723) --- notify/notify.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/notify/notify.go b/notify/notify.go index 30861a3027..1d7597c9c6 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -288,7 +288,7 @@ func NewMetrics(r prometheus.Registerer, ff featurecontrol.Flagger) *Metrics { numNotificationSuppressedTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: "alertmanager", Name: "notifications_suppressed_total", - Help: "The total number of notifications suppressed for being outside of active time intervals or within muted time intervals.", + Help: "The total number of notifications suppressed for being silenced, inhibited, outside of active time intervals or within muted time intervals.", }, []string{"reason"}), notificationLatencySeconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: "alertmanager", @@ -514,10 +514,10 @@ func (n *GossipSettleStage) Exec(ctx context.Context, _ log.Logger, alerts ...*t } const ( - suppressedReasonSilence = "silence" - suppressedReasonInhibition = "inhibition" - suppressedReasonMuteTimeInterval = "mute_time_interval" - suppressedReasonActiveTimeInterval = "active_time_interval" + SuppressedReasonSilence = "silence" + SuppressedReasonInhibition = "inhibition" + SuppressedReasonMuteTimeInterval = "mute_time_interval" + SuppressedReasonActiveTimeInterval = "active_time_interval" ) // MuteStage filters alerts through a Muter. @@ -548,17 +548,17 @@ func (n *MuteStage) Exec(ctx context.Context, logger log.Logger, alerts ...*type // TODO(fabxc): increment muted alerts counter if muted. } if len(muted) > 0 { - level.Debug(logger).Log("msg", "Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted)) var reason string switch n.muter.(type) { case *silence.Silencer: - reason = suppressedReasonSilence + reason = SuppressedReasonSilence case *inhibit.Inhibitor: - reason = suppressedReasonInhibition + reason = SuppressedReasonInhibition default: } n.metrics.numNotificationSuppressedTotal.WithLabelValues(reason).Add(float64(len(muted))) + level.Debug(logger).Log("msg", "Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted), "reason", reason) } return ctx, filtered, nil @@ -953,7 +953,7 @@ func (tms TimeMuteStage) Exec(ctx context.Context, l log.Logger, alerts ...*type // If the current time is inside a mute time, all alerts are removed from the pipeline. if muted { - tms.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonMuteTimeInterval).Add(float64(len(alerts))) + tms.metrics.numNotificationSuppressedTotal.WithLabelValues(SuppressedReasonMuteTimeInterval).Add(float64(len(alerts))) level.Debug(l).Log("msg", "Notifications not sent, route is within mute time", "alerts", len(alerts)) return ctx, nil, nil } @@ -991,7 +991,7 @@ func (tas TimeActiveStage) Exec(ctx context.Context, l log.Logger, alerts ...*ty // If the current time is not inside an active time, all alerts are removed from the pipeline if !muted { - tas.metrics.numNotificationSuppressedTotal.WithLabelValues(suppressedReasonActiveTimeInterval).Add(float64(len(alerts))) + tas.metrics.numNotificationSuppressedTotal.WithLabelValues(SuppressedReasonActiveTimeInterval).Add(float64(len(alerts))) level.Debug(l).Log("msg", "Notifications not sent, route is not within active time", "alerts", len(alerts)) return ctx, nil, nil } From efa801faf7e1c176b797e30379b840b6521973ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20Kr=C3=B6ger?= Date: Mon, 19 Feb 2024 17:53:59 +0100 Subject: [PATCH 20/36] fix discord & msteams webhook url configuration (#3728) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Luca Kröger --- config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 7f3602e066..35cd06dd13 100644 --- a/config/config.go +++ b/config/config.go @@ -516,8 +516,8 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if discord.HTTPConfig == nil { discord.HTTPConfig = c.Global.HTTPConfig } - if discord.WebhookURL == nil { - return fmt.Errorf("no discord webhook URL provided") + if discord.WebhookURL == nil && len(discord.WebhookURLFile) == 0 { + return fmt.Errorf("no discord webhook URL or URLFile provided") } } for _, webex := range rcv.WebexConfigs { @@ -536,8 +536,8 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if msteams.HTTPConfig == nil { msteams.HTTPConfig = c.Global.HTTPConfig } - if msteams.WebhookURL == nil { - return fmt.Errorf("no msteams webhook URL provided") + if msteams.WebhookURL == nil && len(msteams.WebhookURLFile) == 0 { + return fmt.Errorf("no msteams webhook URL or URLFile provided") } } From 81c3e3bae5e169bd053c4cc5b6170afb46081643 Mon Sep 17 00:00:00 2001 From: Philipp Stehle Date: Wed, 28 Feb 2024 12:10:25 +0100 Subject: [PATCH 21/36] `amtool template render` improve default data (#3725) Signed-off-by: Philipp Stehle --- cli/template_render.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/template_render.go b/cli/template_render.go index 7d9842739a..b02df7c0de 100644 --- a/cli/template_render.go +++ b/cli/template_render.go @@ -22,6 +22,7 @@ import ( "time" "github.com/alecthomas/kingpin/v2" + "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/template" ) @@ -31,7 +32,7 @@ var defaultData = template.Data{ Status: "alertstatus", Alerts: template.Alerts{ template.Alert{ - Status: "alertstatus", + Status: string(model.AlertFiring), Labels: template.KV{ "label1": "value1", "label2": "value2", @@ -51,7 +52,7 @@ var defaultData = template.Data{ Fingerprint: "fingerprint1", }, template.Alert{ - Status: "alertstatus", + Status: string(model.AlertResolved), Labels: template.KV{ "foo": "bar", "baz": "qux", From 89e7f00df31b06d41637cab6f6c3b58fd1b9ae7e Mon Sep 17 00:00:00 2001 From: gotjosh Date: Wed, 28 Feb 2024 14:53:13 +0000 Subject: [PATCH 22/36] Cut 0.27 main (#3742) * Cut Alertmanager version 0.27 from the rc.0 (#3740) Signed-off-by: gotjosh * update the release md to reflect the current timings Signed-off-by: gotjosh --------- Signed-off-by: gotjosh --- CHANGELOG.md | 2 +- RELEASE.md | 3 ++- VERSION | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db2d56a29..056bf7416a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.27.0-rc.0 / 2024-02-14 +## 0.27.0 / 2024-02-28 * [CHANGE] Discord Integration: Enforce max length in `message`. #3597 * [CHANGE] API: Removal of all `api/v1/` endpoints. These endpoints now log and return a deprecation message and respond with a status code of `410`. #2970 diff --git a/RELEASE.md b/RELEASE.md index cd9a4eca8e..78a45c9148 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -8,7 +8,8 @@ Release cadence of first pre-releases being cut is 12 weeks. | release series | date (year-month-day) | release shepherd | |----------------|-----------------------|-------------------------------| | v0.26 | 2023-08-23 | Josh Abreu (Github: @gotjosh) | -| v0.27 | 2023-11-01 | Josh Abreu (Github: @gotjosh) | +| v0.27 | 2024-02-28 | Josh Abreu (Github: @gotjosh) | +| v0.28 | 2024-05-28 | Josh Abreu (Github: @gotjosh) | If you are interested in volunteering please create a pull request against the [prometheus/alertmanager](https://github.com/prometheus/alertmanager) repository and propose yourself for the release of your choice. diff --git a/VERSION b/VERSION index 205fccd4ff..1b58cc1018 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.27.0-rc.0 +0.27.0 From d1fe4b7f6f1eb09cf9a5a406eb96237ea762aba5 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 29 Feb 2024 09:57:56 +0000 Subject: [PATCH 23/36] Fix code blocks in the docs being blockquoted instead (#3744) Signed-off-by: George Robinson --- docs/configuration.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index b4b421d0d0..b68ac16f2c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -443,13 +443,17 @@ Alertmanager runs in a special mode called fallback mode as its default mode. As In fallback mode, configurations are first parsed as UTF-8 matchers, and if incompatible with the UTF-8 parser, are then parsed as classic matchers. If your Alertmanager configuration contains matchers that are incompatible with the UTF-8 parser, Alertmanager will parse them as classic matchers and log a warning. This warning also includes a suggestion on how to change the matchers from classic matchers to UTF-8 matchers. For example: -> ts=2024-02-11T10:00:00Z caller=parse.go:176 level=warn msg="Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue." input="foo=" origin=config err="end of input: expected label value" suggestion="foo=\"\"" +``` +ts=2024-02-11T10:00:00Z caller=parse.go:176 level=warn msg="Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue." input="foo=" origin=config err="end of input: expected label value" suggestion="foo=\"\"" +``` Here the matcher `foo=` can be made into a valid UTF-8 matcher by double quoting the right hand side of the expression to give `foo=""`. These two matchers are equivalent, however with UTF-8 matchers the right hand side of the matcher is a required field. In rare cases, a configuration can cause disagreement between the UTF-8 and classic parser. This happens when a matcher is valid in both parsers, but due to added support for UTF-8, results in different parsings depending on which parser is used. If your Alertmanager configuration has disagreement, Alertmanager will use the classic parser and log a warning. For example: -> ts=2024-02-11T10:00:00Z caller=parse.go:183 level=warn msg="Matchers input has disagreement" input="qux=\"\\xf0\\x9f\\x99\\x82\"\n" origin=config +``` +ts=2024-02-11T10:00:00Z caller=parse.go:183 level=warn msg="Matchers input has disagreement" input="qux=\"\\xf0\\x9f\\x99\\x82\"\n" origin=config +``` Any occurrences of disagreement should be looked at on a case by case basis as depending on the nature of the disagreement, the configuration might not need updating before enabling UTF-8 strict mode. For example `\xf0\x9f\x99\x82` is the byte sequence for the 🙂 emoji. If the intention is to match a literal 🙂 emoji then no change is required. However, if the intention is to match the literal `\xf0\x9f\x99\x82` then the matcher should be changed to `qux="\\xf0\\x9f\\x99\\x82"`. @@ -457,11 +461,15 @@ Any occurrences of disagreement should be looked at on a case by case basis as d In UTF-8 strict mode, Alertmanager disables support for classic matchers: -> alertmanager --config.file=config.yml --enable-feature="utf8-strict-mode" +``` +alertmanager --config.file=config.yml --enable-feature="utf8-strict-mode" +``` This mode should be enabled for new Alertmanager installations, and existing Alertmanager installations once all warnings of incompatible matchers have been resolved. Alertmanager will not start in UTF-8 strict mode until all the warnings of incompatible matchers have been resolved: -> ts=2024-02-11T10:00:00Z caller=coordinator.go:118 level=error component=configuration msg="Loading configuration file failed" file=config.yml err="end of input: expected label value" +``` +ts=2024-02-11T10:00:00Z caller=coordinator.go:118 level=error component=configuration msg="Loading configuration file failed" file=config.yml err="end of input: expected label value" +``` UTF-8 strict mode will be the default mode of Alertmanager at the end of the transition period. From d85bef20d95cda14a5e0eba749d9aa5d2c825004 Mon Sep 17 00:00:00 2001 From: George Krajcsovits Date: Thu, 29 Feb 2024 15:53:47 +0100 Subject: [PATCH 24/36] feature: add native histogram support to latency metrics (#3737) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note that this does not stop showing classic metrics, for now it is up to the scrape config to decide whether to keep those instead or both. Signed-off-by: György Krajcsovits --- cluster/channel.go | 10 +++++++--- cluster/delegate.go | 9 ++++++--- cmd/alertmanager/main.go | 9 ++++++--- nflog/nflog.go | 8 ++++++-- notify/notify.go | 11 +++++++---- silence/silence.go | 8 ++++++-- 6 files changed, 38 insertions(+), 17 deletions(-) diff --git a/cluster/channel.go b/cluster/channel.go index ba0b834cd1..5548d50819 100644 --- a/cluster/channel.go +++ b/cluster/channel.go @@ -70,9 +70,13 @@ func NewChannel( ConstLabels: prometheus.Labels{"key": key}, }) oversizeGossipDuration := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_oversize_gossip_message_duration_seconds", - Help: "Duration of oversized gossip message requests.", - ConstLabels: prometheus.Labels{"key": key}, + Name: "alertmanager_oversize_gossip_message_duration_seconds", + Help: "Duration of oversized gossip message requests.", + ConstLabels: prometheus.Labels{"key": key}, + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) reg.MustRegister(oversizeGossipDuration, oversizeGossipMessageFailureTotal, oversizeGossipMessageDroppedTotal, oversizeGossipMessageSentTotal) diff --git a/cluster/delegate.go b/cluster/delegate.go index 9957f69b91..edfda10705 100644 --- a/cluster/delegate.go +++ b/cluster/delegate.go @@ -104,9 +104,12 @@ func newDelegate(l log.Logger, reg prometheus.Registerer, p *Peer, retransmit in }, []string{"peer"}, ) nodePingDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Name: "alertmanager_cluster_pings_seconds", - Help: "Histogram of latencies for ping messages.", - Buckets: []float64{.005, .01, .025, .05, .1, .25, .5}, + Name: "alertmanager_cluster_pings_seconds", + Help: "Histogram of latencies for ping messages.", + Buckets: []float64{.005, .01, .025, .05, .1, .25, .5}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }, []string{"peer"}, ) diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index b2938189d5..c3e9d1b239 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -64,9 +64,12 @@ import ( var ( requestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Name: "alertmanager_http_request_duration_seconds", - Help: "Histogram of latencies for HTTP requests.", - Buckets: []float64{.05, 0.1, .25, .5, .75, 1, 2, 5, 20, 60}, + Name: "alertmanager_http_request_duration_seconds", + Help: "Histogram of latencies for HTTP requests.", + Buckets: []float64{.05, 0.1, .25, .5, .75, 1, 2, 5, 20, 60}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }, []string{"handler", "method"}, ) diff --git a/nflog/nflog.go b/nflog/nflog.go index c533dd0e66..6ce12a8e1f 100644 --- a/nflog/nflog.go +++ b/nflog/nflog.go @@ -139,8 +139,12 @@ func newMetrics(r prometheus.Registerer) *metrics { Help: "Number notification log received queries that failed.", }) m.queryDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_nflog_query_duration_seconds", - Help: "Duration of notification log query evaluation.", + Name: "alertmanager_nflog_query_duration_seconds", + Help: "Duration of notification log query evaluation.", + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) m.propagatedMessagesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_nflog_gossip_messages_propagated_total", diff --git a/notify/notify.go b/notify/notify.go index 1d7597c9c6..3752148f41 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -291,10 +291,13 @@ func NewMetrics(r prometheus.Registerer, ff featurecontrol.Flagger) *Metrics { Help: "The total number of notifications suppressed for being silenced, inhibited, outside of active time intervals or within muted time intervals.", }, []string{"reason"}), notificationLatencySeconds: prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: "alertmanager", - Name: "notification_latency_seconds", - Help: "The latency of notifications in seconds.", - Buckets: []float64{1, 5, 10, 15, 20}, + Namespace: "alertmanager", + Name: "notification_latency_seconds", + Help: "The latency of notifications in seconds.", + Buckets: []float64{1, 5, 10, 15, 20}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }, labels), ff: ff, } diff --git a/silence/silence.go b/silence/silence.go index 710323f747..c87ab76e4d 100644 --- a/silence/silence.go +++ b/silence/silence.go @@ -271,8 +271,12 @@ func newMetrics(r prometheus.Registerer, s *Silences) *metrics { Help: "How many silence received queries did not succeed.", }) m.queryDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "alertmanager_silences_query_duration_seconds", - Help: "Duration of silence query evaluation.", + Name: "alertmanager_silences_query_duration_seconds", + Help: "Duration of silence query evaluation.", + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, }) m.propagatedMessagesTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "alertmanager_silences_gossip_messages_propagated_total", From 1eb83c21ebf4515386933298ee07e87f1c67f35e Mon Sep 17 00:00:00 2001 From: Anand Rajagopal Date: Fri, 1 Mar 2024 03:39:01 -0600 Subject: [PATCH 25/36] A small fix to avoid deadlock that can happen as mentioned in issue #3682 (#3715) Signed-off-by: Anand Rajagopal --- provider/mem/mem.go | 1 - provider/mem/mem_test.go | 57 ++++++++++++++++++++++++++++++++++++++++ store/store.go | 3 +-- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/provider/mem/mem.go b/provider/mem/mem.go index 0e39ba1d5f..9240f1c1bd 100644 --- a/provider/mem/mem.go +++ b/provider/mem/mem.go @@ -151,7 +151,6 @@ func max(a, b int) int { func (a *Alerts) Subscribe() provider.AlertIterator { a.mtx.Lock() defer a.mtx.Unlock() - var ( done = make(chan struct{}) alerts = a.alerts.List() diff --git a/provider/mem/mem_test.go b/provider/mem/mem_test.go index a45321844c..85aa97e63d 100644 --- a/provider/mem/mem_test.go +++ b/provider/mem/mem_test.go @@ -135,6 +135,63 @@ func TestAlertsSubscribePutStarvation(t *testing.T) { } } +func TestDeadLock(t *testing.T) { + t0 := time.Now() + t1 := t0.Add(5 * time.Second) + + marker := types.NewMarker(prometheus.NewRegistry()) + // Run gc every 5 milliseconds to increase the possibility of a deadlock with Subscribe() + alerts, err := NewAlerts(context.Background(), marker, 5*time.Millisecond, noopCallback{}, log.NewNopLogger(), nil) + if err != nil { + t.Fatal(err) + } + alertsToInsert := []*types.Alert{} + for i := 0; i < 200+1; i++ { + alertsToInsert = append(alertsToInsert, &types.Alert{ + Alert: model.Alert{ + // Make sure the fingerprints differ + Labels: model.LabelSet{"iteration": model.LabelValue(strconv.Itoa(i))}, + Annotations: model.LabelSet{"foo": "bar"}, + StartsAt: t0, + EndsAt: t1, + GeneratorURL: "http://example.com/prometheus", + }, + UpdatedAt: t0, + Timeout: false, + }) + } + + if err := alerts.Put(alertsToInsert...); err != nil { + t.Fatal("Unable to add alerts") + } + done := make(chan bool) + + // call subscribe repeatedly in a goroutine to increase + // the possibility of a deadlock occurring + go func() { + tick := time.NewTicker(10 * time.Millisecond) + defer tick.Stop() + stopAfter := time.After(1 * time.Second) + for { + select { + case <-tick.C: + alerts.Subscribe() + case <-stopAfter: + done <- true + break + } + } + }() + + select { + case <-done: + // no deadlock + alerts.Close() + case <-time.After(10 * time.Second): + t.Error("Deadlock detected") + } +} + func TestAlertsPut(t *testing.T) { marker := types.NewMarker(prometheus.NewRegistry()) alerts, err := NewAlerts(context.Background(), marker, 30*time.Minute, noopCallback{}, log.NewNopLogger(), nil) diff --git a/store/store.go b/store/store.go index 83f1495a0f..b0734cab54 100644 --- a/store/store.go +++ b/store/store.go @@ -71,8 +71,6 @@ func (a *Alerts) Run(ctx context.Context, interval time.Duration) { func (a *Alerts) gc() { a.Lock() - defer a.Unlock() - var resolved []*types.Alert for fp, alert := range a.c { if alert.Resolved() { @@ -80,6 +78,7 @@ func (a *Alerts) gc() { resolved = append(resolved, alert) } } + a.Unlock() a.cb(resolved) } From 680568b518ddd728f46101952e006e7b13910901 Mon Sep 17 00:00:00 2001 From: Anand Rajagopal Date: Sun, 10 Mar 2024 12:40:58 -0500 Subject: [PATCH 26/36] Send a slice of values to callback function instead of references (#3745) --- provider/mem/mem.go | 4 ++-- store/store.go | 20 +++++++++++++++----- store/store_test.go | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/provider/mem/mem.go b/provider/mem/mem.go index 9240f1c1bd..23bde9ba7a 100644 --- a/provider/mem/mem.go +++ b/provider/mem/mem.go @@ -100,13 +100,13 @@ func NewAlerts(ctx context.Context, m types.Marker, intervalGC time.Duration, al logger: log.With(l, "component", "provider"), callback: alertCallback, } - a.alerts.SetGCCallback(func(alerts []*types.Alert) { + a.alerts.SetGCCallback(func(alerts []types.Alert) { for _, alert := range alerts { // As we don't persist alerts, we no longer consider them after // they are resolved. Alerts waiting for resolved notifications are // held in memory in aggregation groups redundantly. m.Delete(alert.Fingerprint()) - a.callback.PostDelete(alert) + a.callback.PostDelete(&alert) } a.mtx.Lock() diff --git a/store/store.go b/store/store.go index b0734cab54..9b30542f99 100644 --- a/store/store.go +++ b/store/store.go @@ -34,21 +34,21 @@ var ErrNotFound = errors.New("alert not found") type Alerts struct { sync.Mutex c map[model.Fingerprint]*types.Alert - cb func([]*types.Alert) + cb func([]types.Alert) } // NewAlerts returns a new Alerts struct. func NewAlerts() *Alerts { a := &Alerts{ c: make(map[model.Fingerprint]*types.Alert), - cb: func(_ []*types.Alert) {}, + cb: func(_ []types.Alert) {}, } return a } // SetGCCallback sets a GC callback to be executed after each GC. -func (a *Alerts) SetGCCallback(cb func([]*types.Alert)) { +func (a *Alerts) SetGCCallback(cb func([]types.Alert)) { a.Lock() defer a.Unlock() @@ -71,11 +71,21 @@ func (a *Alerts) Run(ctx context.Context, interval time.Duration) { func (a *Alerts) gc() { a.Lock() - var resolved []*types.Alert + var resolved []types.Alert for fp, alert := range a.c { if alert.Resolved() { delete(a.c, fp) - resolved = append(resolved, alert) + resolved = append(resolved, types.Alert{ + Alert: model.Alert{ + Labels: alert.Labels.Clone(), + Annotations: alert.Annotations.Clone(), + StartsAt: alert.StartsAt, + EndsAt: alert.EndsAt, + GeneratorURL: alert.GeneratorURL, + }, + UpdatedAt: alert.UpdatedAt, + Timeout: alert.Timeout, + }) } } a.Unlock() diff --git a/store/store_test.go b/store/store_test.go index 485c39ceb4..1578d6edce 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -79,7 +79,7 @@ func TestGC(t *testing.T) { done = make(chan struct{}) ctx, cancel = context.WithCancel(context.Background()) ) - s.SetGCCallback(func(a []*types.Alert) { + s.SetGCCallback(func(a []types.Alert) { n += len(a) if n >= len(resolved) { cancel() From 9edea05c12f3af16d5299ac1171b83db13a0c2e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:44:21 +0000 Subject: [PATCH 27/36] Bump mdi-material-ui from 7.6.0 to 7.8.0 in /ui/react-app (#3754) Bumps [mdi-material-ui](https://github.com/TeamWertarbyte/mdi-material-ui) from 7.6.0 to 7.8.0. - [Release notes](https://github.com/TeamWertarbyte/mdi-material-ui/releases) - [Commits](https://github.com/TeamWertarbyte/mdi-material-ui/compare/v7.6.0...v7.8.0) --- updated-dependencies: - dependency-name: mdi-material-ui dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 14 +++++++------- ui/react-app/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index 4b54c03155..d8aa5945f7 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -12,7 +12,7 @@ "@emotion/styled": "^11.9.3", "@mui/material": "^5.10.14", "@tanstack/react-query": "^4.7.1", - "mdi-material-ui": "^7.4.0", + "mdi-material-ui": "^7.8.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.3.0", @@ -5536,9 +5536,9 @@ "dev": true }, "node_modules/mdi-material-ui": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.6.0.tgz", - "integrity": "sha512-daqyXS2tVPB9AulKee4LOwf6llx/eiA969HFx0rOGEC2Cgup/nAmHWnN7yYtndhXpvd2cXwov5kjw40HxjjjZg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.8.0.tgz", + "integrity": "sha512-jLZKGDZ94Mav4c3r8Cyehqs1dHOviXuIm7vGGCIyxIf4UsWNiveQTCoCrKG2K63IWCHcMg0EfjLFqh4LGaYvoQ==", "peerDependencies": { "@mui/material": "^5.0.0 || ^5.0.0-rc.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" @@ -12398,9 +12398,9 @@ "dev": true }, "mdi-material-ui": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.6.0.tgz", - "integrity": "sha512-daqyXS2tVPB9AulKee4LOwf6llx/eiA969HFx0rOGEC2Cgup/nAmHWnN7yYtndhXpvd2cXwov5kjw40HxjjjZg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.8.0.tgz", + "integrity": "sha512-jLZKGDZ94Mav4c3r8Cyehqs1dHOviXuIm7vGGCIyxIf4UsWNiveQTCoCrKG2K63IWCHcMg0EfjLFqh4LGaYvoQ==", "requires": {} }, "media-typer": { diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 13f57b0493..42a78c7872 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.9.3", "@mui/material": "^5.10.14", "@tanstack/react-query": "^4.7.1", - "mdi-material-ui": "^7.4.0", + "mdi-material-ui": "^7.8.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.3.0", From abb5abdcdcdd319631047a7946d9f523b7e09da8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:44:32 +0000 Subject: [PATCH 28/36] Bump @types/react-dom from 18.0.11 to 18.2.19 in /ui/react-app (#3753) Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.0.11 to 18.2.19. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 14 +++++++------- ui/react-app/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index d8aa5945f7..fa81fbbf46 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@types/react": "^18.2.51", - "@types/react-dom": "^18.0.0", + "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", "css-loader": "^6.7.1", @@ -1423,9 +1423,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", - "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "dev": true, "dependencies": { "@types/react": "*" @@ -9298,9 +9298,9 @@ } }, "@types/react-dom": { - "version": "18.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", - "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "dev": true, "requires": { "@types/react": "*" diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 42a78c7872..2f655caedf 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@types/react": "^18.2.51", - "@types/react-dom": "^18.0.0", + "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", "css-loader": "^6.7.1", From 761edc9ebef1d80e7309d200b13287a39a028390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:44:45 +0000 Subject: [PATCH 29/36] Bump github.com/aws/aws-sdk-go from 1.50.8 to 1.50.29 (#3748) Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.50.8 to 1.50.29. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.50.8...v1.50.29) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 76aa4a2f4b..6206fce297 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 - github.com/aws/aws-sdk-go v1.50.8 + github.com/aws/aws-sdk-go v1.50.29 github.com/benbjohnson/clock v1.3.5 github.com/cenkalti/backoff/v4 v4.2.1 github.com/cespare/xxhash/v2 v2.2.0 diff --git a/go.sum b/go.sum index 60f1a0a695..81074286a0 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4= -github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.50.29 h1:Ol2FYzesF2tsQrgVSnDWRFI60+FsSqKKdt7MLlZKubc= +github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= From 4b6b3596b496b5c5015ec9c071d21b107b72a21c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:45:22 +0000 Subject: [PATCH 30/36] Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0 (#3750) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.18.0 to 1.19.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/v1.19.0/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 6206fce297..4a2c59cf03 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 github.com/oklog/run v1.1.0 github.com/oklog/ulid v1.3.1 - github.com/prometheus/client_golang v1.18.0 - github.com/prometheus/common v0.46.0 + github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/common v0.48.0 github.com/prometheus/common/assets v0.2.0 github.com/prometheus/common/sigv4 v0.1.0 github.com/prometheus/exporter-toolkit v0.11.0 diff --git a/go.sum b/go.sum index 81074286a0..4aa19a8876 100644 --- a/go.sum +++ b/go.sum @@ -238,8 +238,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -419,8 +419,8 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -432,8 +432,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= -github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= From 54af8b59ab6a3b821d72e7dd28eb4ac42cb8ccac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:45:57 +0000 Subject: [PATCH 31/36] Bump webpack-merge from 5.8.0 to 5.10.0 in /ui/react-app (#3751) Bumps [webpack-merge](https://github.com/survivejs/webpack-merge) from 5.8.0 to 5.10.0. - [Changelog](https://github.com/survivejs/webpack-merge/blob/develop/CHANGELOG.md) - [Commits](https://github.com/survivejs/webpack-merge/compare/v5.8.0...v5.10.0) --- updated-dependencies: - dependency-name: webpack-merge dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/react-app/package-lock.json | 31 ++++++++++++++++++++++++------- ui/react-app/package.json | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/ui/react-app/package-lock.json b/ui/react-app/package-lock.json index fa81fbbf46..1585dc53e5 100644 --- a/ui/react-app/package-lock.json +++ b/ui/react-app/package-lock.json @@ -44,7 +44,7 @@ "webpack-bundle-analyzer": "^4.7.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.11.1", - "webpack-merge": "^5.8.0" + "webpack-merge": "^5.10.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -4097,6 +4097,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -8170,12 +8179,13 @@ } }, "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" }, "engines": { @@ -11364,6 +11374,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -14319,12 +14335,13 @@ } }, "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "requires": { "clone-deep": "^4.0.1", + "flat": "^5.0.2", "wildcard": "^2.0.0" } }, diff --git a/ui/react-app/package.json b/ui/react-app/package.json index 2f655caedf..e1ff99ceab 100644 --- a/ui/react-app/package.json +++ b/ui/react-app/package.json @@ -46,6 +46,6 @@ "webpack-bundle-analyzer": "^4.7.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.11.1", - "webpack-merge": "^5.8.0" + "webpack-merge": "^5.10.0" } } From f82574a3763c21ca9bf2ada7baf69bd09e01d977 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Thu, 14 Mar 2024 13:56:31 +0100 Subject: [PATCH 32/36] docs: better descibe `email_config.to` fromat (#3760) Signed-off-by: Christoph Maser --- config/config_test.go | 2 +- config/notifiers_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ docs/configuration.md | 9 +++++---- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index 7631d37cb7..7aba475f72 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -200,7 +200,7 @@ receivers: func TestTimeIntervalHasName(t *testing.T) { in := ` time_intervals: -- name: +- name: time_intervals: - times: - start_time: '09:00' diff --git a/config/notifiers_test.go b/config/notifiers_test.go index 199d1ce310..de1bb5f96b 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -15,6 +15,8 @@ package config import ( "errors" + "net/mail" + "reflect" "strings" "testing" @@ -60,6 +62,47 @@ headers: } } +func TestEmailToAllowsMultipleAdresses(t *testing.T) { + in := ` +to: 'a@example.com, ,b@example.com,c@example.com' +` + var cfg EmailConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + if err != nil { + t.Fatal(err) + } + + expected := []*mail.Address{ + {Address: "a@example.com"}, + {Address: "b@example.com"}, + {Address: "c@example.com"}, + } + + res, err := mail.ParseAddressList(cfg.To) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(res, expected) { + t.Fatalf("expected %v, got %v", expected, res) + } +} + +func TestEmailDisallowMalformed(t *testing.T) { + in := ` +to: 'a@' +` + var cfg EmailConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + if err != nil { + t.Fatal(err) + } + _, err = mail.ParseAddressList(cfg.To) + if err == nil { + t.Fatalf("no error returned, expected:\n%v", "mail: no angle-addr") + } +} + func TestPagerdutyTestRoutingKey(t *testing.T) { t.Run("error if no routing key or key file", func(t *testing.T) { in := ` diff --git a/docs/configuration.md b/docs/configuration.md index b68ac16f2c..fa9878399c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -487,7 +487,7 @@ You can use this mode if you suspect there is an issue with fallback mode or UTF You can use `amtool` to validate that an Alertmanager configuration file is compatible with UTF-8 strict mode before enabling it in Alertmanager server. You do not need a running Alertmanager server to do this. -Just like Alertmanager server, `amtool` will log a warning if the configuration is incompatible or contains disagreement: +Just like Alertmanager server, `amtool` will log a warning if the configuration is incompatible or contains disagreement: ``` amtool check-config config.yml @@ -503,7 +503,7 @@ Found: - 0 templates ``` -You will know if a configuration is compatible with UTF-8 strict mode when no warnings are logged in `amtool`: +You will know if a configuration is compatible with UTF-8 strict mode when no warnings are logged in `amtool`: ``` amtool check-config config.yml @@ -644,7 +644,7 @@ Here are some more examples: ``` As shown below, in the short-form, it's better to use double quotes to avoid problems with special characters like commas: - + ```yaml matchers: [ "foo = \"bar,baz\"", "dings != bums" ] ``` @@ -867,6 +867,7 @@ webhook_url_file: [ send_resolved: | default = false ] # The email address to send notifications to. +# Allows a comma separated list of rfc5322 compliant email addresses. to: # The sender's address. @@ -1458,6 +1459,6 @@ room_id: # Message template. [ message: default = '{{ template "webex.default.message" .}}' ] -# The HTTP client's configuration. You must use this configuration to supply the bot token as part of the HTTP `Authorization` header. +# The HTTP client's configuration. You must use this configuration to supply the bot token as part of the HTTP `Authorization` header. [ http_config: | default = global.http_config ] ``` From 342f6a599ce16c138663f18ed0b880e777c3017d Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 21 Mar 2024 11:26:46 +0000 Subject: [PATCH 33/36] Add godot linter (#3613) * Add godot linter Signed-off-by: George Robinson * Remove extra line from LICENSE Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- .golangci.yml | 8 ++++++++ api/v1_deprecation_router.go | 1 - api/v2/api.go | 8 ++++---- cli/check_config.go | 2 +- cli/cluster.go | 2 +- cli/config.go | 2 +- cli/config/http_config.go | 2 +- cli/format/format_extended.go | 6 +++--- cli/root.go | 4 ++-- cli/silence.go | 2 +- cli/utils.go | 8 ++++---- cluster/cluster.go | 6 +++--- config/config.go | 2 +- config/notifiers.go | 4 ++-- matchers/parse/lexer.go | 2 +- matchers/parse/parse.go | 8 ++++++-- nflog/nflog.go | 2 +- notify/email/email.go | 2 +- notify/notify.go | 4 ++-- notify/util.go | 4 ++-- notify/webex/webex.go | 1 + pkg/labels/parse.go | 2 +- provider/provider.go | 2 +- test/cli/acceptance.go | 1 + test/cli/collector.go | 2 +- test/with_api_v2/collector.go | 2 +- test/with_api_v2/mock.go | 2 +- timeinterval/timeinterval.go | 4 ++-- timeinterval/timeinterval_test.go | 4 ++-- types/types.go | 2 +- 30 files changed, 57 insertions(+), 44 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d98ec74670..b6463d0638 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,6 +11,7 @@ linters: enable: - depguard - errorlint + - godot - gofumpt - goimports - misspell @@ -51,6 +52,13 @@ linters-settings: - (github.com/go-kit/log.Logger).Log # Never check for rollback errors as Rollback() is called when a previous error was detected. - (github.com/prometheus/prometheus/storage.Appender).Rollback + godot: + scope: toplevel + exclude: + - "^ ?This file is safe to edit" + - "^ ?scheme value" + period: true + capital: true goimports: local-prefixes: github.com/prometheus/alertmanager gofumpt: diff --git a/api/v1_deprecation_router.go b/api/v1_deprecation_router.go index 3ebbbd076f..46a61ebf74 100644 --- a/api/v1_deprecation_router.go +++ b/api/v1_deprecation_router.go @@ -8,7 +8,6 @@ // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific l package api diff --git a/api/v2/api.go b/api/v2/api.go index b4f57e75e2..6f034d2596 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -53,7 +53,7 @@ import ( "github.com/prometheus/alertmanager/types" ) -// API represents an Alertmanager API v2 +// API represents an Alertmanager API v2. type API struct { peer cluster.ClusterPeer silences *silence.Silences @@ -82,7 +82,7 @@ type ( setAlertStatusFn func(prometheus_model.LabelSet) ) -// NewAPI returns a new Alertmanager API v2 +// NewAPI returns a new Alertmanager API v2. func NewAPI( alerts provider.Alerts, gf groupsFn, @@ -545,9 +545,9 @@ var silenceStateOrder = map[types.SilenceState]int{ // SortSilences sorts first according to the state "active, pending, expired" // then by end time or start time depending on the state. -// active silences should show the next to expire first +// Active silences should show the next to expire first // pending silences are ordered based on which one starts next -// expired are ordered based on which one expired most recently +// expired are ordered based on which one expired most recently. func SortSilences(sils open_api_models.GettableSilences) { sort.Slice(sils, func(i, j int) bool { state1 := types.SilenceState(*sils[i].Status.State) diff --git a/cli/check_config.go b/cli/check_config.go index 77214c8737..c024c27542 100644 --- a/cli/check_config.go +++ b/cli/check_config.go @@ -23,7 +23,7 @@ import ( "github.com/prometheus/alertmanager/template" ) -// TODO: This can just be a type that is []string, doesn't have to be a struct +// TODO: This can just be a type that is []string, doesn't have to be a struct. type checkConfigCmd struct { files []string } diff --git a/cli/cluster.go b/cli/cluster.go index 9c72373c80..eefc729a49 100644 --- a/cli/cluster.go +++ b/cli/cluster.go @@ -24,7 +24,7 @@ import ( const clusterHelp = `View cluster status and peers.` -// clusterCmd represents the cluster command +// configureClusterCmd represents the cluster command. func configureClusterCmd(app *kingpin.Application) { clusterCmd := app.Command("cluster", clusterHelp) clusterCmd.Command("show", clusterHelp).Default().Action(execWithTimeout(showStatus)).PreAction(requireAlertManagerURL) diff --git a/cli/config.go b/cli/config.go index 2f01e2c3b5..4fd936898c 100644 --- a/cli/config.go +++ b/cli/config.go @@ -30,7 +30,7 @@ The amount of output is controlled by the output selection flag: - Json: Print entire config object as json ` -// configCmd represents the config command +// configureConfigCmd represents the config command. func configureConfigCmd(app *kingpin.Application) { configCmd := app.Command("config", configHelp) configCmd.Command("show", configHelp).Default().Action(execWithTimeout(queryConfig)).PreAction(requireAlertManagerURL) diff --git a/cli/config/http_config.go b/cli/config/http_config.go index 71b656b16a..3704e7e611 100644 --- a/cli/config/http_config.go +++ b/cli/config/http_config.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" ) -// LoadHTTPConfigFile returns HTTPClientConfig for the given http_config file +// LoadHTTPConfigFile returns HTTPClientConfig for the given http_config file. func LoadHTTPConfigFile(filename string) (*promconfig.HTTPClientConfig, error) { b, err := os.ReadFile(filename) if err != nil { diff --git a/cli/format/format_extended.go b/cli/format/format_extended.go index 919ef9c211..3870cc7db9 100644 --- a/cli/format/format_extended.go +++ b/cli/format/format_extended.go @@ -37,7 +37,7 @@ func (formatter *ExtendedFormatter) SetOutput(writer io.Writer) { formatter.writer = writer } -// FormatSilences formats the silences into a readable string +// FormatSilences formats the silences into a readable string. func (formatter *ExtendedFormatter) FormatSilences(silences []models.GettableSilence) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByEndAt(silences)) @@ -58,7 +58,7 @@ func (formatter *ExtendedFormatter) FormatSilences(silences []models.GettableSil return w.Flush() } -// FormatAlerts formats the alerts into a readable string +// FormatAlerts formats the alerts into a readable string. func (formatter *ExtendedFormatter) FormatAlerts(alerts []*models.GettableAlert) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByStartsAt(alerts)) @@ -78,7 +78,7 @@ func (formatter *ExtendedFormatter) FormatAlerts(alerts []*models.GettableAlert) return w.Flush() } -// FormatConfig formats the alertmanager status information into a readable string +// FormatConfig formats the alertmanager status information into a readable string. func (formatter *ExtendedFormatter) FormatConfig(status *models.AlertmanagerStatus) error { fmt.Fprintln(formatter.writer, status.Config.Original) fmt.Fprintln(formatter.writer, "buildUser", status.VersionInfo.BuildUser) diff --git a/cli/root.go b/cli/root.go index 69c1022c6a..fe02fb82f3 100644 --- a/cli/root.go +++ b/cli/root.go @@ -89,7 +89,7 @@ const ( defaultAmApiv2path = "/api/v2" ) -// NewAlertmanagerClient initializes an alertmanager client with the given URL +// NewAlertmanagerClient initializes an alertmanager client with the given URL. func NewAlertmanagerClient(amURL *url.URL) *client.AlertmanagerAPI { address := defaultAmHost + ":" + defaultAmPort schemes := []string{"http"} @@ -145,7 +145,7 @@ func NewAlertmanagerClient(amURL *url.URL) *client.AlertmanagerAPI { return c } -// Execute is the main function for the amtool command +// Execute is the main function for the amtool command. func Execute() { app := kingpin.New("amtool", helpRoot).UsageWriter(os.Stdout) diff --git a/cli/silence.go b/cli/silence.go index a3373a8677..d7ed2ce4a6 100644 --- a/cli/silence.go +++ b/cli/silence.go @@ -17,7 +17,7 @@ import ( "github.com/alecthomas/kingpin/v2" ) -// silenceCmd represents the silence command +// configureSilenceCmd represents the silence command. func configureSilenceCmd(app *kingpin.Application) { silenceCmd := app.Command("silence", "Add, expire or view silences. For more information and additional flags see query help").PreAction(requireAlertManagerURL) configureSilenceAddCmd(silenceCmd) diff --git a/cli/utils.go b/cli/utils.go index dfe39dd867..7215220af2 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -29,7 +29,7 @@ import ( "github.com/prometheus/alertmanager/pkg/labels" ) -// getRemoteAlertmanagerConfigStatus returns status responsecontaining configuration from remote Alertmanager +// getRemoteAlertmanagerConfigStatus returns status responsecontaining configuration from remote Alertmanager. func getRemoteAlertmanagerConfigStatus(ctx context.Context, alertmanagerURL *url.URL) (*models.AlertmanagerStatus, error) { amclient := NewAlertmanagerClient(alertmanagerURL) params := general.NewGetStatusParams().WithContext(ctx) @@ -69,7 +69,7 @@ func loadAlertmanagerConfig(ctx context.Context, alertmanagerURL *url.URL, confi return config.Load(*configStatus.Config.Original) } -// convertClientToCommonLabelSet converts client.LabelSet to model.Labelset +// convertClientToCommonLabelSet converts client.LabelSet to model.Labelset. func convertClientToCommonLabelSet(cls models.LabelSet) model.LabelSet { mls := make(model.LabelSet, len(cls)) for ln, lv := range cls { @@ -78,7 +78,7 @@ func convertClientToCommonLabelSet(cls models.LabelSet) model.LabelSet { return mls } -// TypeMatchers only valid for when you are going to add a silence +// TypeMatchers only valid for when you are going to add a silence. func TypeMatchers(matchers []labels.Matcher) models.Matchers { typeMatchers := make(models.Matchers, len(matchers)) for i, matcher := range matchers { @@ -87,7 +87,7 @@ func TypeMatchers(matchers []labels.Matcher) models.Matchers { return typeMatchers } -// TypeMatcher only valid for when you are going to add a silence +// TypeMatcher only valid for when you are going to add a silence. func TypeMatcher(matcher labels.Matcher) *models.Matcher { name := matcher.Name value := matcher.Value diff --git a/cluster/cluster.go b/cluster/cluster.go index d85ee7f533..6e8bdaacd5 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -42,7 +42,7 @@ type ClusterPeer interface { Peers() []ClusterMember } -// ClusterMember interface that represents node peers in a cluster +// ClusterMember interface that represents node peers in a cluster. type ClusterMember interface { // Name returns the name of the node Name() string @@ -639,10 +639,10 @@ type Member struct { node *memberlist.Node } -// Name implements cluster.ClusterMember +// Name implements cluster.ClusterMember. func (m Member) Name() string { return m.node.Name } -// Address implements cluster.ClusterMember +// Address implements cluster.ClusterMember. func (m Member) Address() string { return m.node.Address() } // Peers returns the peers in the cluster. diff --git a/config/config.go b/config/config.go index 35cd06dd13..b1760ea64b 100644 --- a/config/config.go +++ b/config/config.go @@ -972,7 +972,7 @@ func (re Regexp) MarshalYAML() (interface{}, error) { return nil, nil } -// UnmarshalJSON implements the json.Unmarshaler interface for Regexp +// UnmarshalJSON implements the json.Unmarshaler interface for Regexp. func (re *Regexp) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { diff --git a/config/notifiers.go b/config/notifiers.go index d79c8b5057..7d52aed1a8 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -312,13 +312,13 @@ type PagerdutyConfig struct { Group string `yaml:"group,omitempty" json:"group,omitempty"` } -// PagerdutyLink is a link +// PagerdutyLink is a link. type PagerdutyLink struct { Href string `yaml:"href,omitempty" json:"href,omitempty"` Text string `yaml:"text,omitempty" json:"text,omitempty"` } -// PagerdutyImage is an image +// PagerdutyImage is an image. type PagerdutyImage struct { Src string `yaml:"src,omitempty" json:"src,omitempty"` Alt string `yaml:"alt,omitempty" json:"alt,omitempty"` diff --git a/matchers/parse/lexer.go b/matchers/parse/lexer.go index d6daa6a9e8..52bb03925e 100644 --- a/matchers/parse/lexer.go +++ b/matchers/parse/lexer.go @@ -251,7 +251,7 @@ func (l *lexer) accept(valid string) bool { } // expect consumes the next rune if its one of the valid runes. -// it returns nil if the next rune is valid, otherwise an expectedError +// It returns nil if the next rune is valid, otherwise an expectedError // error. func (l *lexer) expect(valid string) error { if strings.ContainsRune(valid, l.next()) { diff --git a/matchers/parse/parse.go b/matchers/parse/parse.go index 30a95b2554..34e36203c0 100644 --- a/matchers/parse/parse.go +++ b/matchers/parse/parse.go @@ -196,7 +196,7 @@ func (p *parser) parseEndOfMatcher(l *lexer) (parseFunc, error) { if err != nil { if errors.Is(err, errEOF) { // If this is the end of input we still need to check if the optional - // open brace has a matching close brace + // open brace has a matching close brace. return p.parseCloseBrace, nil } return nil, fmt.Errorf("%w: %w", err, errExpectedCommaOrCloseBrace) @@ -220,7 +220,7 @@ func (p *parser) parseComma(l *lexer) (parseFunc, error) { if err != nil { if errors.Is(err, errEOF) { // If this is the end of input we still need to check if the optional - // open brace has a matching close brace + // open brace has a matching close brace. return p.parseCloseBrace, nil } return nil, fmt.Errorf("%w: %w", err, errExpectedMatcherOrCloseBrace) @@ -242,6 +242,7 @@ func (p *parser) parseEOF(l *lexer) (parseFunc, error) { return nil, nil } +// nolint:godot // accept returns true if the next token is one of the specified kinds, // otherwise false. If the token is accepted it is consumed. tokenEOF is // not an accepted kind and instead accept returns ErrEOF if there is no @@ -256,6 +257,7 @@ func (p *parser) accept(l *lexer, kinds ...tokenKind) (ok bool, err error) { return ok, err } +// nolint:godot // acceptPeek returns true if the next token is one of the specified kinds, // otherwise false. However, unlike accept, acceptPeek does not consume accepted // tokens. tokenEOF is not an accepted kind and instead accept returns ErrEOF @@ -271,6 +273,7 @@ func (p *parser) acceptPeek(l *lexer, kinds ...tokenKind) (bool, error) { return t.isOneOf(kinds...), nil } +// nolint:godot // expect returns the next token if it is one of the specified kinds, otherwise // it returns an error. If the token is expected it is consumed. tokenEOF is not // an accepted kind and instead expect returns ErrEOF if there is no more input. @@ -285,6 +288,7 @@ func (p *parser) expect(l *lexer, kind ...tokenKind) (token, error) { return t, nil } +// nolint:godot // expect returns the next token if it is one of the specified kinds, otherwise // it returns an error. However, unlike expect, expectPeek does not consume tokens. // tokenEOF is not an accepted kind and instead expect returns ErrEOF if there is no diff --git a/nflog/nflog.go b/nflog/nflog.go index 6ce12a8e1f..3084bf00a0 100644 --- a/nflog/nflog.go +++ b/nflog/nflog.go @@ -46,7 +46,7 @@ var ErrInvalidState = errors.New("invalid state") // query currently allows filtering by and/or receiver group key. // It is configured via QueryParameter functions. // -// TODO(fabxc): Future versions could allow querying a certain receiver +// TODO(fabxc): Future versions could allow querying a certain receiver, // group or a given time interval. type query struct { recv *pb.Receiver diff --git a/notify/email/email.go b/notify/email/email.go index d7e5be7bdc..98c98c17a8 100644 --- a/notify/email/email.go +++ b/notify/email/email.go @@ -347,7 +347,7 @@ func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { return "LOGIN", []byte{}, nil } -// Used for AUTH LOGIN. (Maybe password should be encrypted) +// Used for AUTH LOGIN. (Maybe password should be encrypted). func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { if more { switch strings.ToLower(string(fromServer)) { diff --git a/notify/notify.go b/notify/notify.go index 3752148f41..bd4b4faea5 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -469,7 +469,7 @@ func (ms MultiStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Al return ctx, alerts, nil } -// FanoutStage executes its stages concurrently +// FanoutStage executes its stages concurrently. type FanoutStage []Stage // Exec attempts to execute all stages concurrently and discards the results. @@ -616,7 +616,7 @@ func utcNow() time.Time { return time.Now().UTC() } -// Wrap a slice in a struct so we can store a pointer in sync.Pool +// Wrap a slice in a struct so we can store a pointer in sync.Pool. type hashBuffer struct { buf []byte } diff --git a/notify/util.go b/notify/util.go index bd54e2b47d..0c440e6528 100644 --- a/notify/util.go +++ b/notify/util.go @@ -34,7 +34,7 @@ import ( // truncationMarker is the character used to represent a truncation. const truncationMarker = "…" -// UserAgentHeader is the default User-Agent for notification requests +// UserAgentHeader is the default User-Agent for notification requests. var UserAgentHeader = fmt.Sprintf("Alertmanager/%s", version.Version) // RedactURL removes the URL part from an error of *url.Error type. @@ -47,7 +47,7 @@ func RedactURL(err error) error { return e } -// Get sends a GET request to the given URL +// Get sends a GET request to the given URL. func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { return request(ctx, client, http.MethodGet, url, "", nil) } diff --git a/notify/webex/webex.go b/notify/webex/webex.go index 9e95e3ed9b..347dd63a47 100644 --- a/notify/webex/webex.go +++ b/notify/webex/webex.go @@ -30,6 +30,7 @@ import ( ) const ( + // nolint:godot // maxMessageSize represents the maximum message length that Webex supports. maxMessageSize = 7439 ) diff --git a/pkg/labels/parse.go b/pkg/labels/parse.go index 5138716e0a..f84b29e84b 100644 --- a/pkg/labels/parse.go +++ b/pkg/labels/parse.go @@ -136,7 +136,7 @@ func ParseMatcher(s string) (_ *Matcher, err error) { return nil, fmt.Errorf("matcher value not valid UTF-8: %s", ms[3]) } - // Unescape the rawValue: + // Unescape the rawValue. for i, r := range rawValue { if escaped { escaped = false diff --git a/provider/provider.go b/provider/provider.go index 5fcce20360..4d1f76f206 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -47,7 +47,7 @@ type AlertIterator interface { Next() <-chan *types.Alert } -// NewAlertIterator returns a new AlertIterator based on the generic alertIterator type +// NewAlertIterator returns a new AlertIterator based on the generic alertIterator type. func NewAlertIterator(ch <-chan *types.Alert, done chan struct{}, err error) AlertIterator { return &alertIterator{ ch: ch, diff --git a/test/cli/acceptance.go b/test/cli/acceptance.go index d799c2e24a..a2d00885a9 100644 --- a/test/cli/acceptance.go +++ b/test/cli/acceptance.go @@ -41,6 +41,7 @@ import ( ) const ( + // nolint:godot // amtool is the relative path to local amtool binary. amtool = "../../../amtool" ) diff --git a/test/cli/collector.go b/test/cli/collector.go index a48cd90903..c3b251a399 100644 --- a/test/cli/collector.go +++ b/test/cli/collector.go @@ -191,7 +191,7 @@ func alertsToString(as []*models.GettableAlert) (string, error) { return string(b), nil } -// CompareCollectors compares two collectors based on their collected alerts +// CompareCollectors compares two collectors based on their collected alerts. func CompareCollectors(a, b *Collector, opts *AcceptanceOpts) (bool, error) { f := func(collected map[float64][]models.GettableAlerts) []*models.GettableAlert { result := []*models.GettableAlert{} diff --git a/test/with_api_v2/collector.go b/test/with_api_v2/collector.go index b0e9adc4fc..755d292f4c 100644 --- a/test/with_api_v2/collector.go +++ b/test/with_api_v2/collector.go @@ -191,7 +191,7 @@ func alertsToString(as []*models.GettableAlert) (string, error) { return string(b), nil } -// CompareCollectors compares two collectors based on their collected alerts +// CompareCollectors compares two collectors based on their collected alerts. func CompareCollectors(a, b *Collector, opts *AcceptanceOpts) (bool, error) { f := func(collected map[float64][]models.GettableAlerts) []*models.GettableAlert { result := []*models.GettableAlert{} diff --git a/test/with_api_v2/mock.go b/test/with_api_v2/mock.go index 836199e919..0ac4428976 100644 --- a/test/with_api_v2/mock.go +++ b/test/with_api_v2/mock.go @@ -78,7 +78,7 @@ func (s *TestSilence) Match(v ...string) *TestSilence { return s } -// MatchRE adds a new regex matcher to the silence +// MatchRE adds a new regex matcher to the silence. func (s *TestSilence) MatchRE(v ...string) *TestSilence { if len(v)%2 == 1 { panic("bad key/values") diff --git a/timeinterval/timeinterval.go b/timeinterval/timeinterval.go index fe8c97d729..4522a22b8c 100644 --- a/timeinterval/timeinterval.go +++ b/timeinterval/timeinterval.go @@ -383,7 +383,7 @@ func (r WeekdayRange) MarshalYAML() (interface{}, error) { // MarshalText implements the econding.TextMarshaler interface for WeekdayRange. // It converts the range into a colon-separated string, or a single weekday if possible. -// e.g. "monday:friday" or "saturday". +// E.g. "monday:friday" or "saturday". func (r WeekdayRange) MarshalText() ([]byte, error) { beginStr, ok := daysOfWeekInv[r.Begin] if !ok { @@ -450,7 +450,7 @@ func (tz Location) MarshalJSON() (out []byte, err error) { // MarshalText implements the encoding.TextMarshaler interface for InclusiveRange. // It converts the struct into a colon-separated string, or a single element if -// appropriate. e.g. "monday:friday" or "monday" +// appropriate. E.g. "monday:friday" or "monday". func (ir InclusiveRange) MarshalText() ([]byte, error) { if ir.Begin == ir.End { return []byte(strconv.Itoa(ir.Begin)), nil diff --git a/timeinterval/timeinterval_test.go b/timeinterval/timeinterval_test.go index 8377ac119f..8750ca2a41 100644 --- a/timeinterval/timeinterval_test.go +++ b/timeinterval/timeinterval_test.go @@ -561,7 +561,7 @@ func TestYamlMarshal(t *testing.T) { } // Test JSON marshalling by marshalling a time interval -// and then unmarshalling to ensure they're identical +// and then unmarshalling to ensure they're identical. func TestJsonMarshal(t *testing.T) { for _, tc := range yamlUnmarshalTestCases { if tc.expectError { @@ -624,7 +624,7 @@ months: ['february'] }, } -// Tests the entire flow from unmarshalling to containing a time +// Tests the entire flow from unmarshalling to containing a time. func TestTimeIntervalComplete(t *testing.T) { for _, tc := range completeTestCases { var ti TimeInterval diff --git a/types/types.go b/types/types.go index 54a889ab9c..49b9f0b486 100644 --- a/types/types.go +++ b/types/types.go @@ -458,7 +458,7 @@ type Silence struct { } // Expired return if the silence is expired -// meaning that both StartsAt and EndsAt are equal +// meaning that both StartsAt and EndsAt are equal. func (s *Silence) Expired() bool { return s.StartsAt.Equal(s.EndsAt) } From 6c70b5c0143b60103d05581b48961a60538d6060 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 21 Mar 2024 20:54:56 +0000 Subject: [PATCH 34/36] Silences: Add benchmarks for Mutes (#3771) * Add benchmarks for Mutes This commit updates the existing benchmarks for silences to also benchmark Mutes. This complements the existing Query benchmarks by also measuring the time taken to mark silenced alerts. --------- Signed-off-by: George Robinson --- silence/silence_bench_test.go | 157 ++++++++++++++++++++++++++++++++++ silence/silence_test.go | 65 -------------- 2 files changed, 157 insertions(+), 65 deletions(-) create mode 100644 silence/silence_bench_test.go diff --git a/silence/silence_bench_test.go b/silence/silence_bench_test.go new file mode 100644 index 0000000000..146316e3f3 --- /dev/null +++ b/silence/silence_bench_test.go @@ -0,0 +1,157 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package silence + +import ( + "strconv" + "testing" + "time" + + "github.com/benbjohnson/clock" + "github.com/go-kit/log" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/model" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/silence/silencepb" + "github.com/prometheus/alertmanager/types" +) + +// BenchmarkMutes benchmarks the Mutes method for the Muter interface for +// different numbers of silences, where all silences match the alert. +func BenchmarkMutes(b *testing.B) { + b.Run("1 silence mutes alert", func(b *testing.B) { + benchmarkMutes(b, 1) + }) + b.Run("10 silences mute alert", func(b *testing.B) { + benchmarkMutes(b, 10) + }) + b.Run("100 silences mute alert", func(b *testing.B) { + benchmarkMutes(b, 100) + }) + b.Run("1000 silences mute alert", func(b *testing.B) { + benchmarkMutes(b, 1000) + }) + b.Run("10000 silences mute alert", func(b *testing.B) { + benchmarkMutes(b, 10000) + }) +} + +func benchmarkMutes(b *testing.B, n int) { + silences, err := New(Options{}) + require.NoError(b, err) + + clock := clock.NewMock() + silences.clock = clock + now := clock.Now() + + var silenceIDs []string + for i := 0; i < n; i++ { + var silenceID string + silenceID, err = silences.Set(&silencepb.Silence{ + Matchers: []*silencepb.Matcher{{ + Type: silencepb.Matcher_EQUAL, + Name: "foo", + Pattern: "bar", + }}, + StartsAt: now, + EndsAt: now.Add(time.Minute), + }) + require.NoError(b, err) + silenceIDs = append(silenceIDs, silenceID) + } + require.Len(b, silenceIDs, n) + + m := types.NewMarker(prometheus.NewRegistry()) + s := NewSilencer(silences, m, log.NewNopLogger()) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + s.Mutes(model.LabelSet{"foo": "bar"}) + } + b.StopTimer() + + // The alert should be marked as silenced for each silence. + activeIDs, pendingIDs, _, silenced := m.Silenced(model.LabelSet{"foo": "bar"}.Fingerprint()) + require.True(b, silenced) + require.Empty(b, pendingIDs) + require.Len(b, activeIDs, n) +} + +// BenchmarkQuery benchmarks the Query method for the Silences struct +// for different numbers of silences. Not all silences match the query +// to prevent compiler and runtime optimizations from affecting the benchmarks. +func BenchmarkQuery(b *testing.B) { + b.Run("100 silences", func(b *testing.B) { + benchmarkQuery(b, 100) + }) + b.Run("1000 silences", func(b *testing.B) { + benchmarkQuery(b, 1000) + }) + b.Run("10000 silences", func(b *testing.B) { + benchmarkQuery(b, 10000) + }) +} + +func benchmarkQuery(b *testing.B, numSilences int) { + s, err := New(Options{}) + require.NoError(b, err) + + clock := clock.NewMock() + s.clock = clock + now := clock.Now() + + lset := model.LabelSet{"aaaa": "AAAA", "bbbb": "BBBB", "cccc": "CCCC"} + + s.st = state{} + for i := 0; i < numSilences; i++ { + id := strconv.Itoa(i) + // Include an offset to avoid optimizations. + patA := "A{4}|" + id + patB := id // Does not match. + if i%10 == 0 { + // Every 10th time, have an actually matching pattern. + patB = "B(B|C)B.|" + id + } + + s.st[id] = &silencepb.MeshSilence{Silence: &silencepb.Silence{ + Id: id, + Matchers: []*silencepb.Matcher{ + {Type: silencepb.Matcher_REGEXP, Name: "aaaa", Pattern: patA}, + {Type: silencepb.Matcher_REGEXP, Name: "bbbb", Pattern: patB}, + }, + StartsAt: now.Add(-time.Minute), + EndsAt: now.Add(time.Hour), + UpdatedAt: now.Add(-time.Hour), + }} + } + + // Run things once to populate the matcherCache. + sils, _, err := s.Query( + QState(types.SilenceStateActive), + QMatches(lset), + ) + require.NoError(b, err) + require.Len(b, sils, numSilences/10) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + sils, _, err := s.Query( + QState(types.SilenceStateActive), + QMatches(lset), + ) + require.NoError(b, err) + require.Len(b, sils, numSilences/10) + } +} diff --git a/silence/silence_test.go b/silence/silence_test.go index 2a880dd3b6..89bd6ac6f6 100644 --- a/silence/silence_test.go +++ b/silence/silence_test.go @@ -15,7 +15,6 @@ package silence import ( "bytes" - "fmt" "os" "runtime" "sort" @@ -1596,70 +1595,6 @@ func TestStateDecodingError(t *testing.T) { require.Equal(t, ErrInvalidState, err) } -func benchmarkSilencesQuery(b *testing.B, numSilences int) { - s, err := New(Options{}) - require.NoError(b, err) - - clock := clock.NewMock() - s.clock = clock - now := clock.Now() - - lset := model.LabelSet{"aaaa": "AAAA", "bbbb": "BBBB", "cccc": "CCCC"} - - s.st = state{} - for i := 0; i < numSilences; i++ { - id := fmt.Sprint("ID", i) - // Patterns also contain the ID to bust any caches that might be used under the hood. - patA := "A{4}|" + id - patB := id // Does not match. - if i%10 == 0 { - // Every 10th time, have an actually matching pattern. - patB = "B(B|C)B.|" + id - } - - s.st[id] = &pb.MeshSilence{Silence: &pb.Silence{ - Id: id, - Matchers: []*pb.Matcher{ - {Type: pb.Matcher_REGEXP, Name: "aaaa", Pattern: patA}, - {Type: pb.Matcher_REGEXP, Name: "bbbb", Pattern: patB}, - }, - StartsAt: now.Add(-time.Minute), - EndsAt: now.Add(time.Hour), - UpdatedAt: now.Add(-time.Hour), - }} - } - - // Run things once to populate the matcherCache. - sils, _, err := s.Query( - QState(types.SilenceStateActive), - QMatches(lset), - ) - require.NoError(b, err) - require.Len(b, sils, numSilences/10) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - sils, _, err := s.Query( - QState(types.SilenceStateActive), - QMatches(lset), - ) - require.NoError(b, err) - require.Len(b, sils, numSilences/10) - } -} - -func Benchmark100SilencesQuery(b *testing.B) { - benchmarkSilencesQuery(b, 100) -} - -func Benchmark1000SilencesQuery(b *testing.B) { - benchmarkSilencesQuery(b, 1000) -} - -func Benchmark10000SilencesQuery(b *testing.B) { - benchmarkSilencesQuery(b, 10000) -} - // runtime.Gosched() does not "suspend" the current goroutine so there's no guarantee that the main goroutine won't // be able to continue. For more see https://pkg.go.dev/runtime#Gosched. func gosched() { From f41fccb730f0cef4ede906d0e9b1a545a3fe151c Mon Sep 17 00:00:00 2001 From: George Robinson Date: Thu, 21 Mar 2024 20:58:48 +0000 Subject: [PATCH 35/36] Inhibition Rules: Add benchmarks for Mutes (#3772) * Add benchmarks for Mutes This commit adds benchmarks for Mutes in the inhibit package. Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- inhibit/inhibit_bench_test.go | 180 ++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 inhibit/inhibit_bench_test.go diff --git a/inhibit/inhibit_bench_test.go b/inhibit/inhibit_bench_test.go new file mode 100644 index 0000000000..ec09860eb3 --- /dev/null +++ b/inhibit/inhibit_bench_test.go @@ -0,0 +1,180 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package inhibit + +import ( + "context" + "errors" + "strconv" + "testing" + "time" + + "github.com/go-kit/log" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/model" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/provider/mem" + "github.com/prometheus/alertmanager/types" +) + +// BenchmarkMutes benchmarks the Mutes method for the Muter interface +// for different numbers of inhibition rules. +func BenchmarkMutes(b *testing.B) { + b.Run("1 inhibition rule, 1 inhibiting alert", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1, 1)) + }) + b.Run("10 inhibition rules, 1 inhibiting alert", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 10, 1)) + }) + b.Run("100 inhibition rules, 1 inhibiting alert", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 100, 1)) + }) + b.Run("1000 inhibition rules, 1 inhibiting alert", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1000, 1)) + }) + b.Run("10000 inhibition rules, 1 inhibiting alert", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 10000, 1)) + }) + b.Run("1 inhibition rule, 10 inhibiting alerts", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1, 10)) + }) + b.Run("1 inhibition rule, 100 inhibiting alerts", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1, 100)) + }) + b.Run("1 inhibition rule, 1000 inhibiting alerts", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1, 1000)) + }) + b.Run("1 inhibition rule, 10000 inhibiting alerts", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 1, 10000)) + }) + b.Run("100 inhibition rules, 1000 inhibiting alerts", func(b *testing.B) { + benchmarkMutes(b, defaultBenchmark(b, 100, 1000)) + }) +} + +// benchmarkOptions allows the declaration of a wide range of benchmarks. +type benchmarkOptions struct { + // n is the total number of inhibition rules. + n int + // newRuleFunc creates the next inhibition rule. It is called n times. + newRuleFunc func(idx int) config.InhibitRule + // newAlertsFunc creates the inhibiting alerts for each inhibition rule. + // It is called n times. + newAlertsFunc func(idx int, r config.InhibitRule) []types.Alert + // benchFunc runs the benchmark. + benchFunc func(mutesFunc func(model.LabelSet) bool) error +} + +// defaultBenchmark returns the default benchmark. It supports a number of +// variations, including customization of the number of inhibition rules, +// and the number of inhibiting alerts per inhibition rule. +// +// The source matchers are suffixed with the position of the inhibition rule +// in the list. For example, src=1, src=2, etc. The target matchers are +// the same across all inhibition rules (dst=0). +// +// Each inhibition rule can have zero or more alerts that match the source +// matchers, and is determined with numInhibitingAlerts. +// +// The default benchmark expects dst=0 to be muted and will fail if not. +func defaultBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlerts int) benchmarkOptions { + return benchmarkOptions{ + n: numInhibitionRules, + newRuleFunc: func(idx int) config.InhibitRule { + return config.InhibitRule{ + SourceMatchers: config.Matchers{ + mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)), + }, + TargetMatchers: config.Matchers{ + mustNewMatcher(b, labels.MatchEqual, "dst", "0"), + }, + } + }, + newAlertsFunc: func(idx int, _ config.InhibitRule) []types.Alert { + var alerts []types.Alert + for i := 0; i < numInhibitingAlerts; i++ { + alerts = append(alerts, types.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{ + "src": model.LabelValue(strconv.Itoa(idx)), + "idx": model.LabelValue(strconv.Itoa(i)), + }, + }, + }) + } + return alerts + }, benchFunc: func(mutesFunc func(set model.LabelSet) bool) error { + if ok := mutesFunc(model.LabelSet{"dst": "0"}); !ok { + return errors.New("expected dst=0 to be muted") + } + return nil + }, + } +} + +func benchmarkMutes(b *testing.B, opts benchmarkOptions) { + r := prometheus.NewRegistry() + m := types.NewMarker(r) + s, err := mem.NewAlerts(context.TODO(), m, time.Minute, nil, log.NewNopLogger(), r) + if err != nil { + b.Fatal(err) + } + defer s.Close() + + alerts, rules := benchmarkFromOptions(opts) + for _, a := range alerts { + tmp := a + if err = s.Put(&tmp); err != nil { + b.Fatal(err) + } + } + + ih := NewInhibitor(s, rules, m, log.NewNopLogger()) + defer ih.Stop() + go ih.Run() + + // Wait some time for the inhibitor to seed its cache. + waitDuration := time.Millisecond * time.Duration(len(alerts)) + if waitDuration > time.Second { + waitDuration = time.Second + } + <-time.After(waitDuration) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + require.NoError(b, opts.benchFunc(ih.Mutes)) + } +} + +func benchmarkFromOptions(opts benchmarkOptions) ([]types.Alert, []config.InhibitRule) { + var ( + alerts = make([]types.Alert, 0, opts.n) + rules = make([]config.InhibitRule, 0, opts.n) + ) + for i := 0; i < opts.n; i++ { + r := opts.newRuleFunc(i) + alerts = append(alerts, opts.newAlertsFunc(i, r)...) + rules = append(rules, r) + } + return alerts, rules +} + +func mustNewMatcher(b *testing.B, op labels.MatchType, name, value string) *labels.Matcher { + m, err := labels.NewMatcher(op, name, value) + require.NoError(b, err) + return m +} From 14cbe6301c732658d6fe877ec55ad5b738abcf06 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Fri, 22 Mar 2024 09:07:19 +0000 Subject: [PATCH 36/36] Add more benchmarks for inhibition rules (#3773) * Add more benchmarks for inhibition rules This commit adds more benchmarks for inhibition rules where just the last rule in the benchmark inhibits the labels. Signed-off-by: George Robinson --------- Signed-off-by: George Robinson --- inhibit/inhibit_bench_test.go | 95 +++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/inhibit/inhibit_bench_test.go b/inhibit/inhibit_bench_test.go index ec09860eb3..2aa881ea32 100644 --- a/inhibit/inhibit_bench_test.go +++ b/inhibit/inhibit_bench_test.go @@ -35,34 +35,46 @@ import ( // for different numbers of inhibition rules. func BenchmarkMutes(b *testing.B) { b.Run("1 inhibition rule, 1 inhibiting alert", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1, 1)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1, 1)) }) b.Run("10 inhibition rules, 1 inhibiting alert", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 10, 1)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 10, 1)) }) b.Run("100 inhibition rules, 1 inhibiting alert", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 100, 1)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 100, 1)) }) b.Run("1000 inhibition rules, 1 inhibiting alert", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1000, 1)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1000, 1)) }) b.Run("10000 inhibition rules, 1 inhibiting alert", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 10000, 1)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 10000, 1)) }) b.Run("1 inhibition rule, 10 inhibiting alerts", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1, 10)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1, 10)) }) b.Run("1 inhibition rule, 100 inhibiting alerts", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1, 100)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1, 100)) }) b.Run("1 inhibition rule, 1000 inhibiting alerts", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1, 1000)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1, 1000)) }) b.Run("1 inhibition rule, 10000 inhibiting alerts", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 1, 10000)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 1, 10000)) }) b.Run("100 inhibition rules, 1000 inhibiting alerts", func(b *testing.B) { - benchmarkMutes(b, defaultBenchmark(b, 100, 1000)) + benchmarkMutes(b, allRulesMatchBenchmark(b, 100, 1000)) + }) + b.Run("10 inhibition rules, last rule matches", func(b *testing.B) { + benchmarkMutes(b, lastRuleMatchesBenchmark(b, 10)) + }) + b.Run("100 inhibition rules, last rule matches", func(b *testing.B) { + benchmarkMutes(b, lastRuleMatchesBenchmark(b, 100)) + }) + b.Run("1000 inhibition rules, last rule matches", func(b *testing.B) { + benchmarkMutes(b, lastRuleMatchesBenchmark(b, 1000)) + }) + b.Run("10000 inhibition rules, last rule matches", func(b *testing.B) { + benchmarkMutes(b, lastRuleMatchesBenchmark(b, 10000)) }) } @@ -79,19 +91,20 @@ type benchmarkOptions struct { benchFunc func(mutesFunc func(model.LabelSet) bool) error } -// defaultBenchmark returns the default benchmark. It supports a number of -// variations, including customization of the number of inhibition rules, -// and the number of inhibiting alerts per inhibition rule. +// allRulesMatchBenchmark returns a new benchmark where all inhibition rules +// inhibit the label dst=0. It supports a number of variations, including +// customization of the number of inhibition rules, and the number of +// inhibiting alerts per inhibition rule. // // The source matchers are suffixed with the position of the inhibition rule -// in the list. For example, src=1, src=2, etc. The target matchers are -// the same across all inhibition rules (dst=0). +// in the list (e.g. src=1, src=2, etc...). The target matchers are the same +// across all inhibition rules (dst=0). // // Each inhibition rule can have zero or more alerts that match the source // matchers, and is determined with numInhibitingAlerts. // -// The default benchmark expects dst=0 to be muted and will fail if not. -func defaultBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlerts int) benchmarkOptions { +// It expects dst=0 to be muted and will fail if not. +func allRulesMatchBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlerts int) benchmarkOptions { return benchmarkOptions{ n: numInhibitionRules, newRuleFunc: func(idx int) config.InhibitRule { @@ -126,6 +139,48 @@ func defaultBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlerts int) } } +// lastRuleMatchesBenchmark returns a new benchmark where the last inhibition +// rule inhibits the label dst=0. All other inhibition rules are no-ops. +// +// The source matchers are suffixed with the position of the inhibition rule +// in the list (e.g. src=1, src=2, etc...). The target matchers are the same +// across all inhibition rules (dst=0). +// +// It expects dst=0 to be muted and will fail if not. +func lastRuleMatchesBenchmark(b *testing.B, n int) benchmarkOptions { + return benchmarkOptions{ + n: n, + newRuleFunc: func(idx int) config.InhibitRule { + return config.InhibitRule{ + SourceMatchers: config.Matchers{ + mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)), + }, + TargetMatchers: config.Matchers{ + mustNewMatcher(b, labels.MatchEqual, "dst", "0"), + }, + } + }, + newAlertsFunc: func(idx int, _ config.InhibitRule) []types.Alert { + // Do not create an alert unless it is the last inhibition rule. + if idx < n-1 { + return nil + } + return []types.Alert{{ + Alert: model.Alert{ + Labels: model.LabelSet{ + "src": model.LabelValue(strconv.Itoa(idx)), + }, + }, + }} + }, benchFunc: func(mutesFunc func(set model.LabelSet) bool) error { + if ok := mutesFunc(model.LabelSet{"dst": "0"}); !ok { + return errors.New("expected dst=0 to be muted") + } + return nil + }, + } +} + func benchmarkMutes(b *testing.B, opts benchmarkOptions) { r := prometheus.NewRegistry() m := types.NewMarker(r) @@ -148,11 +203,7 @@ func benchmarkMutes(b *testing.B, opts benchmarkOptions) { go ih.Run() // Wait some time for the inhibitor to seed its cache. - waitDuration := time.Millisecond * time.Duration(len(alerts)) - if waitDuration > time.Second { - waitDuration = time.Second - } - <-time.After(waitDuration) + <-time.After(time.Second) b.ResetTimer() for i := 0; i < b.N; i++ {