commit 34222954b0d01208796eb6572e5afe5bcb9dfd0d Author: Markus Pesch Date: Sun Jan 19 20:43:51 2025 +0100 Initial Commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b53e68c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.gitea/workflows/generate-readme.yaml b/.gitea/workflows/generate-readme.yaml new file mode 100644 index 0000000..b55c2c4 --- /dev/null +++ b/.gitea/workflows/generate-readme.yaml @@ -0,0 +1,30 @@ +name: Generate README + +on: + pull_request: + paths: [ "README.md", "values.yaml" ] + types: [ "opened", "reopened", "synchronize" ] + push: + paths: [ "README.md", "values.yaml" ] + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + generate-parameters: + container: + image: docker.io/library/node:22.13.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Generate parameter section in README + run: | + npm install + npm run readme:parameters + - name: Compare diff + run: git diff --exit-code --name-only README.md diff --git a/.gitea/workflows/helm.yaml b/.gitea/workflows/helm.yaml new file mode 100644 index 0000000..cb9dc6a --- /dev/null +++ b/.gitea/workflows/helm.yaml @@ -0,0 +1,40 @@ +name: Helm + +on: + pull_request: + types: [ "opened", "reopened", "synchronize" ] + push: + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + helm-lint: + container: + image: docker.io/volkerraschek/helm:3.16.4 + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Lint helm files + run: | + helm lint --values values.yaml . + + helm-unittest: + container: + image: docker.io/volkerraschek/helm:3.16.4 + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Unittest + run: | + helm unittest --strict --file 'unittests/**/*.yaml' ./ \ No newline at end of file diff --git a/.gitea/workflows/markdown-linters.yaml b/.gitea/workflows/markdown-linters.yaml new file mode 100644 index 0000000..7168acf --- /dev/null +++ b/.gitea/workflows/markdown-linters.yaml @@ -0,0 +1,44 @@ +name: Markdown linter + +on: + pull_request: + paths: [ "**/*.md" ] + types: [ "opened", "reopened", "synchronize" ] + push: + paths: [ "**/*.md" ] + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + markdown-link-checker: + container: + image: docker.io/library/node:22.13.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Verify links in markdown files + run: | + npm install + npm run readme:link + + markdown-lint: + container: + image: docker.io/library/node:22.13.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git + - uses: actions/checkout@v4.2.2 + - name: Lint markdown files + run: | + npm install + npm run readme:lint diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml new file mode 100644 index 0000000..6b1c0e2 --- /dev/null +++ b/.gitea/workflows/release.yaml @@ -0,0 +1,46 @@ +name: Release + +on: + push: + tags: + - "**" + +jobs: + publish-chart: + container: + image: docker.io/volkerraschek/helm:3.16.4 + runs-on: ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4 + - name: Package chart + env: + HELM_REPO_NAME: upload + + CHARTMUSEUM_PASSWORD: ${{ secrets.CHARTMUSEUM_PASSWORD }} + CHARTMUSEUM_REPOSITORY: ${{ vars.CHARTMUSEUM_REPOSITORY }} + CHARTMUSEUM_USERNAME: ${{ secrets.CHARTMUSEUM_USERNAME }} + CHARTMUSEUM_HOSTNAME: ${{ vars.CHARTMUSEUM_HOSTNAME }} + + GITEA_PACKAGE_REGISTRY_TOKEN: ${{ secrets.GIT_CRYPTIC_SYSTEMS_PACKAGE_REGISTRY_TOKEN }} + GITEA_SERVER_URL: ${{ github.server_url }} + run: | + PACKAGE_VERSION=${GITHUB_REF#refs/tags/} + REPOSITORY_NAME=$(echo ${GITHUB_REPOSITORY} | cut -d '/' -f 2) + REPOSITORY_OWNER=$(echo ${GITHUB_REPOSITORY} | cut -d '/' -f 1) + + helm dependency build + helm package --version "${PACKAGE_VERSION}" ./ + + # chart-museum + helm repo add --username ${CHARTMUSEUM_USERNAME} --password ${CHARTMUSEUM_PASSWORD} chartmuseum https://${CHARTMUSEUM_HOSTNAME}/${CHARTMUSEUM_REPOSITORY} + helm cm-push ${REPOSITORY_NAME}-${PACKAGE_VERSION}.tgz chartmuseum + helm repo remove chartmuseum + + # gitea + helm repo add --username ${REPOSITORY_OWNER} --password ${GITEA_PACKAGE_REGISTRY_TOKEN} gitea ${GITEA_SERVER_URL}/api/packages/${REPOSITORY_OWNER}/helm + helm cm-push ${REPOSITORY_NAME}-${PACKAGE_VERSION}.tgz gitea + helm repo remove gitea \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba69fff --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +charts +node_modules +target +values2.yml +values2.yaml +*.tgz diff --git a/.helmignore b/.helmignore new file mode 100644 index 0000000..a954747 --- /dev/null +++ b/.helmignore @@ -0,0 +1,60 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store + +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ + +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ + +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# drone +.drone.yml + +# editorconfig +.editorconfig + +# customized values +values2.yml +values2.yaml + +# helm packages +*.tgz +.helmignore +unittests + +# markdownlint +.markdownlint.yml +.markdownlint.yaml +.markdownlintignore + +# npm +.prettierignore +.npmrc +package* + +# yamllint +.yamllint.yaml + +# Others +CONTRIBUTING.md +CODEOWNERS +Makefile +renovate.json diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..5156987 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,156 @@ +# markdownlint YAML configuration +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "atx" + +# MD004/ul-style - Unordered list style +MD004: + style: "dash" + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 120 + # Number of characters for headings + heading_line_length: 120 + # Number of characters for code blocks + code_block_line_length: 80 + # Include code blocks + code_blocks: false + # Include tables + tables: false + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: + # Only check sibling headings + siblings_only: true + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "---" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: + - Git + - GitDevOps + - Gitea + - GitHub + - GitLab + - GitOps + - kube-prometheus-stack + - Memcached + - Oracle + - ORBIS U + - PostgreSQL + - Prometheus + - prometheus-exporter + - SSL + - TLS + # Include code blocks + code_blocks: false + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: "fenced" + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence syle + style: "backtick" diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..9fe4803 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,4 @@ +.github/ +Chart.lock +charts/ +node_modules/ \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..fafeafa --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +Chart.lock \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..08a5b51 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "DavidAnson.vscode-markdownlint", + "esbenp.prettier-vscode", + "Tim-Koehler.helm-intellisense", + "yzhang.markdown-all-in-one" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7ed7716 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "yaml.schemas": { + "https://raw.githubusercontent.com/helm-unittest/helm-unittest/v0.5.2/schema/helm-testsuite.json": [ + "/unittests/**/*.yaml" + ] + }, + "yaml.schemaStore.enable": true +} \ No newline at end of file diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..90128be --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,20 @@ +--- +extends: default + +ignore: | + .yamllint + node_modules + templates + + +rules: + truthy: + allowed-values: ['true', 'false'] + check-keys: False + level: error + line-length: disable + document-start: disable + comments: + min-spaces-from-content: 1 + braces: + max-spaces-inside: 2 \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..af45fc8 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @volker.raschek diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6347fe2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,82 @@ +# Contributing + +I am very happy if you would like to provide a pull request 👍 + +The content of this file describes which requirements contributors should fulfill before submitting a pull request (PR). + +1. [Valid Git commits](#valid-git-commits) + +## Valid Git commits + +### Commit message + +The repository is subject to a strict commit message template. This states that there are several types of commits. For +example, `fix`, `chore`, `refac`, `test` or `doc`. All types are described in more detail below. + +| type | description | +| ------------------- | ----------------------------------------------------------------- | +| `feat` | New feature. | +| `fix` | Fixes a bug. | +| `refac` | Refactoring production code. | +| `style` | Fixes formatting issues. No production code change. | +| `docs` | Adapt documentation. No production code change. | +| `test` | Adds new or modifies existing tests. No production code change. | +| `chore` | Updating grunt tasks. Is everything which the user does not see. | + +Based on these types, commit messaged can then be created. Here are a few examples: + +```text +style(README): Wrong indentation +feat(deployment): support restartPolicy +fix(my-app): Add missing volume +docs(CONTRIBUTING): Describe how to commit correctly +``` + +This type of commit message makes it easier for me as maintainer to keep an overview and does not cause the commits of a +pull request PR to be combined into one commit (squashing). + +### Smart commits + +Smart commits are excellent when it comes to tracking bugs or issues. In this repository, however, the rebasing of +commits is prohibited, which means that only merge commits are possible. This means that a smart commit message only +needs to be added to the merge commit. + +This has the advantage that the maintainer can use the smart commit to find the merge commit and undo the entire history +of a merge without having to select individual commits. The following history illustrates the correct use of smart commits. + +```text +* 823edbc7 Volker Raschek (G) | [Close #2] feat(deployment): support additional containers +|\ +| * 321aebc3 Volker Raschek (G) | doc(README): generate README with new deployment attributes +| * 8d101dd3 Volker Raschek (G) | test(deployment): Extend unittest of additional containers +| * 6f2abd93 Volker Raschek (G) | fix(deployment): Extend deployment of additional containers +|/ +* aa5ebda bob (N) | [Close #1] feat(deployment): support initContainers +``` + +### Commit signing + +Another problem with Git is the chain of trust. Git allows the configuration of any name and e-mail address. An attacker +can impersonate any person and submit pull requests under a false identity. For as Linux Torvalds, the maintainer of the +Linux kernel. + +```bash +git config --global user.name 'Linux Torvalds' +git config --global user.email 'torvalds@linux-foundation.org' +``` + +To avoid this, some Git repositories expect signed commits. In particular, repositories that are subject to direct +delivery to customers. For this reason, the repository is subject to a branch protection rule that only allows signed +commits. *Until* there is *no verified* and *no signed* commit, the pull request is blocked. + +The following articles describes how Git can be configured to sign commits. Please keep in mind, that the e-mail +address, which is used as UID of the GPG keyring must also be defined in the profile settings of your GitHub account. +Otherwise will be marked the Git commit as *Unverified*. + +1. [Signing Commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) +2. [Tell Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key) + +Inspect your Git commit via `git log`. There should be mentioned, that your commit is signed. + +Furthermore, the GPG key is unique. **Don't loose your private GPG key**. Backup your private key on a safe device. For +example an external USB drive. diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..d432ab2 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: prometheus-fail2ban-exporter +description: Prometheus metric exporter for Fail2Ban +type: application +kubeVersion: ">=1.20.0" +version: "0.1.0" +appVersion: "0.1.0" + +# icon: https://annotations.example.com/icon.png + +keywords: +- prometheus +- prometheus-exporter +- prometheus-fail2ban-exporter +- fail2ban-exporter + +sources: +- https://git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter-charts +- https://git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6bb1e0e --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# CONTAINER_RUNTIME +CONTAINER_RUNTIME?=$(shell which podman) + +# HELM_IMAGE +HELM_IMAGE_REGISTRY_HOST?=docker.io +HELM_IMAGE_REPOSITORY?=volkerraschek/helm +HELM_IMAGE_VERSION?=3.16.1 # renovate: datasource=docker registryUrl=https://docker.io depName=volkerraschek/helm +HELM_IMAGE_FULLY_QUALIFIED=${HELM_IMAGE_REGISTRY_HOST}/${HELM_IMAGE_REPOSITORY}:${HELM_IMAGE_VERSION} + +# NODE_IMAGE +NODE_IMAGE_REGISTRY_HOST?=docker.io +NODE_IMAGE_REPOSITORY?=library/node +NODE_IMAGE_VERSION?=22.9.0-alpine # renovate: datasource=docker registryUrl=https://docker.io depName=library/node +NODE_IMAGE_FULLY_QUALIFIED=${NODE_IMAGE_REGISTRY_HOST}/${NODE_IMAGE_REPOSITORY}:${NODE_IMAGE_VERSION} + +# MISSING DOT +# ============================================================================== +missing-dot: + grep --perl-regexp '## @(param|skip).*[^.]$$' values.yaml + +# CONTAINER RUN - README +# ============================================================================== +PHONY+=container-run/readme +container-run/readme: container-run/readme/link container-run/readme/lint container-run/readme/parameters + +container-run/readme/link: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:link + +container-run/readme/lint: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:lint + +container-run/readme/parameters: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:parameters + +# CONTAINER RUN - HELM UNITTESTS +# ============================================================================== +PHONY+=container-run/helm-unittests +container-run/helm-unittests: + ${CONTAINER_RUNTIME} run \ + --env HELM_REPO_PASSWORD=${CHART_SERVER_PASSWORD} \ + --env HELM_REPO_USERNAME=${CHART_SERVER_USERNAME} \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + unittest --strict --file 'unittests/**/*.yaml' ./ + +# CONTAINER RUN - HELM UPDATE DEPENDENCIES +# ============================================================================== +PHONY+=container-run/helm-update-dependencies +container-run/helm-update-dependencies: + ${CONTAINER_RUNTIME} run \ + --env HELM_REPO_PASSWORD=${CHART_SERVER_PASSWORD} \ + --env HELM_REPO_USERNAME=${CHART_SERVER_USERNAME} \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + dependency update + +# CONTAINER RUN - MARKDOWN-LINT +# ============================================================================== +PHONY+=container-run/helm-lint +container-run/helm-lint: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + lint --values values.yaml . + +# PHONY +# ============================================================================== +# Declare the contents of the PHONY variable as phony. We keep that information +# in a variable so we can use it in if_changed. +.PHONY: ${PHONY} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..805157b --- /dev/null +++ b/README.md @@ -0,0 +1,317 @@ +# Prometheus Fail2Ban exporter + +[![Build Status](https://drone.cryptic.systems/api/badges/volker.raschek/prometheus-fail2ban-exporter/status.svg)](https://drone.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter) +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/prometheus-exporters)](https://artifacthub.io/packages/search?repo=prometheus-exporters) + +This helm chart enables the deployment of a Prometheus metrics exporter for Fail2Ban and allows the individual +configuration of additional containers/initContainers, mounting of volumes and defining additional environment variables, +apply a user-defined `webConfig.yaml` and much more. + +Chapter [configuration and installation](#helm-configuration-and-installation) describes the basics how to configure helm +and use it to deploy the exporter. It also contains further configuration examples. + +Furthermore, this helm chart contains unit tests to detect regressions and stabilize the deployment. Additionally, this +helm chart is tested for deployment scenarios with **ArgoCD**. + +## Helm: configuration and installation + +1. A helm chart repository must be configured, to pull the helm charts from. +2. All available parameters are [here](#parameters) in detail document. The parameters can be defined via the helm + `--set` flag or directly as part of a `values.yaml` file. The following example defines the `prometheus-exporter` + repository and use the `--set` flag for a basic deployment. + +> [!IMPORTANT] +> By default is neither a serviceMonitor nor a podMonitor enabled. Use `prometheus.metrics.serviceMonitor.enabled=true` +> or `prometheus.metrics.podMonitor.enabled=true` to enable one monitor deployment. Deploying both monitors at the same +> time is not possible. + +```bash +helm repo add prometheus-exporters https://charts.cryptic.systems/prometheus-exporters +helm repo update +helm install prometheus-fail2ban-exporter prometheus-exporters/prometheus-fail2ban-exporter \ + --set 'prometheus.metrics.enabled=true' \ + --set 'prometheus.metrics.serviceMonitor.enabled=true' +``` + +Instead of passing all parameters via the *set* flag, it is also possible to define them as part of the `values.yaml`. +The following command downloads the `values.yaml` for a specific version of this chart. Please keep in mind, that the +version of the chart must be in sync with the `values.yaml`. Newer *minor* versions can have new features. New *major* +versions can break something! + +```bash +CHART_VERSION=0.1.0 +helm show values prometheus-exporters/prometheus-fail2ban-exporter --version "${CHART_VERSION}" > values.yaml +``` + +A complete list of available helm chart versions can be displayed via the following command: + +```bash +helm search repo prometheus-fail2ban-exporter --versions +``` + +The helm chart also contains some prometheusRules. These are deactivated by default and serve as examples/inspiration +for customizations. These can be configured in more detail via `values.yaml`. + +### Examples + +The following examples serve as individual configurations and as inspiration for how deployment problems can be solved. + +#### Avoid CPU throttling by defining a CPU limit + +If the application is deployed with a CPU resource limit, Prometheus may throw a CPU throttling warning for the +application. This has more or less to do with the fact that the application finds the number of CPUs of the host, but +cannot use the available CPU time to perform computing operations. + +The application must be informed that despite several CPUs only a part (limit) of the available computing time is +available. As this is a Golang application, this can be implemented using `GOMAXPROCS`. The following example is one way +of defining `GOMAXPROCS` automatically based on the defined CPU limit like `100m`. Please keep in mind, that the CFS +rate of `100ms` - default on each kubernetes node, is also very important to avoid CPU throttling. + +Further information about this topic can be found [here](https://kanishk.io/posts/cpu-throttling-in-containerized-go-apps/). + +> [!NOTE] +> The environment variable `GOMAXPROCS` is set automatically, when a CPU limit is defined. An explicit configuration is +> not anymore required. + +```bash +helm install prometheus-fail2ban-exporter prometheus-exporters/prometheus-fail2ban-exporter \ + --set 'prometheus.metrics.enabled=true' \ + --set 'prometheus.metrics.serviceMonitor.enabled=true' \ + --set 'daemonSet.fail2banExporter.env.name=GOMAXPROCS' \ + --set 'daemonSet.fail2banExporter.env.valueFrom.resourceFieldRef.resource=limits.cpu' \ + --set 'daemonSet.fail2banExporter.resources.limits.cpu=100m' +``` + + + +#### Grafana dashboard + +The helm chart includes Grafana dashboards. These can be deployed as a configMap by activating Grafana integration. It +is assumed that the dashboard is consumed by Grafana or a sidecar container itself and that the dashboard is stored in +the Grafana container file system so that it is subsequently available to the user. The +[kube-prometheus-stack](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack) deployment +makes this possible. + +```bash +helm install prometheus-fail2ban-exporter prometheus-exporters/prometheus-fail2ban-exporter \ + --set 'grafana.enabled=true' +``` + +#### Avoid deploying on same node / bare metal host as PostgresDB + +As a best practice, avoid running the fail2ban-exporter on the same node / bare-metal host as the PostgresDB. This is +because if the fail2ban-exporter is running on the same node and this node fails, Prometheus can send an alert about the +failure of the node or that the fail2ban-exporter cannot be reached. However, it is not possible to react based on the +metrics that the fail2ban-exporter explicitly provides. Depending on the configuration of alerts, this may mean that the +corresponding notifications are not sent to the right person or group of people. + +The following example prevent the fail2ban-exporter from running on nodes with a PostgresDB. The PostgresDB nodes has an +additional label `database=fail2ban`. The configuration is carried out in `values.yaml`. + +```yaml +deployment: + affinity: + nodeAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: + - key: database + operator: NotIn + values: + - fail2ban +``` + +## Parameters + +### Global + +| Name | Description | Value | +| ------------------ | ----------------------------------------- | ----- | +| `nameOverride` | Individual release name suffix. | `""` | +| `fullnameOverride` | Override the complete release name logic. | `""` | + +### Configuration + +| Name | Description | Value | +| -------------------------------------------- | --------------------------------------------------------------------- | ------- | +| `config.webConfig.existingSecret.enabled` | Mount an existing secret containing the key `webConfig.yaml`. | `false` | +| `config.webConfig.existingSecret.secretName` | Name of the existing secret containing the key `webConfig.yaml`. | `""` | +| `config.webConfig.secret.annotations` | Additional annotations of the secret containing the `webConfig.yaml`. | `{}` | +| `config.webConfig.secret.labels` | Additional labels of the secret containing the `webConfig.yaml`. | `{}` | +| `config.webConfig.secret.webConfig` | Content of the `webConfig.yaml`. | `{}` | + +### Daemonset + +| Name | Description | Value | +| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| `daemonSet.annotations` | Additional deployment annotations. | `{}` | +| `daemonSet.labels` | Additional deployment labels. | `{}` | +| `daemonSet.additionalContainers` | List of additional containers. | `[]` | +| `daemonSet.affinity` | Affinity for the fail2ban-exporter daemonSet. | `{}` | +| `daemonSet.initContainers` | List of additional init containers. | `[]` | +| `daemonSet.dnsConfig` | dnsConfig of the fail2ban-exporter daemonSet. | `{}` | +| `daemonSet.dnsPolicy` | dnsPolicy of the fail2ban-exporter daemonSet. | `""` | +| `daemonSet.hostname` | Individual hostname of the pod. | `""` | +| `daemonSet.subdomain` | Individual domain of the pod. | `""` | +| `daemonSet.hostNetwork` | Use the kernel network namespace of the host system. | `false` | +| `daemonSet.imagePullSecrets` | Secret to use for pulling the image. | `[]` | +| `daemonSet.fail2banExporter.args` | Arguments passed to the fail2ban-exporter container. | `[]` | +| `daemonSet.fail2banExporter.env` | List of environment variables for the fail2ban-exporter container. | `[]` | +| `daemonSet.fail2banExporter.envFrom` | List of environment variables mounted from configMaps or secrets for the fail2ban-exporter container. | `[]` | +| `daemonSet.fail2banExporter.image.registry` | Image registry, eg. `docker.io`. | `git.cryptic.systems` | +| `daemonSet.fail2banExporter.image.repository` | Image repository, eg. `library/busybox`. | `volker.raschek/prometheus-fail2ban-exporter` | +| `daemonSet.fail2banExporter.image.tag` | Custom image tag, eg. `0.1.0`. Defaults to `appVersion`. | `""` | +| `daemonSet.fail2banExporter.image.pullPolicy` | Image pull policy. | `IfNotPresent` | +| `daemonSet.fail2banExporter.resources` | CPU and memory resources of the pod. | `{}` | +| `daemonSet.fail2banExporter.securityContext` | Security context of the container of the daemonSet. | `{}` | +| `daemonSet.fail2banExporter.volumeMounts` | Additional volume mounts. | `undefined` | +| `daemonSet.nodeSelector` | NodeSelector of the fail2ban-exporter daemonSet. | `{}` | +| `daemonSet.priorityClassName` | PriorityClassName of the fail2ban-exporter daemonSet. | `""` | +| `daemonSet.restartPolicy` | Restart policy of the fail2ban-exporter daemonSet. | `""` | +| `daemonSet.securityContext` | Security context of the fail2ban-exporter daemonSet. | `{}` | +| `daemonSet.strategy.type` | Strategy type - `Recreate` or `Rollingupdate`. | `Recreate` | +| `daemonSet.strategy.rollingUpdate.maxSurge` | The maximum number of pods that can be scheduled above the desired number of pods during a rolling update. | `1` | +| `daemonSet.strategy.rollingUpdate.maxUnavailable` | The maximum number of pods that can be unavailable during a rolling update. | `1` | +| `daemonSet.terminationGracePeriodSeconds` | How long to wait until forcefully kill the pod. | `60` | +| `daemonSet.tolerations` | Tolerations of the fail2ban-exporter daemonSet. | `[]` | +| `daemonSet.topologySpreadConstraints` | TopologySpreadConstraints of the fail2ban-exporter daemonSet. | `[]` | +| `daemonSet.volumes` | Additional volumes to mount into the pods of the prometheus-exporter daemonset. | `undefined` | + +### Grafana + +| Name | Description | Value | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------- | +| `grafana.enabled` | Enable integration into Grafana. Require the Prometheus operator daemonSet. | `false` | +| `grafana.dashboardDiscoveryLabels` | Labels that Grafana uses to discover resources. The labels may vary depending on the Grafana daemonSet. | `undefined` | +| `grafana.dashboards.fail2banExporter.enabled` | Enable deployment of Grafana dashboard `fail2banExporter`. | `true` | +| `grafana.dashboards.fail2banExporter.annotations` | Additional configmap annotations. | `{}` | +| `grafana.dashboards.fail2banExporter.labels` | Additional configmap labels. | `{}` | + +### Ingress + +| Name | Description | Value | +| --------------------- | -------------------------------------------------------------------------------------------------------------------- | ------- | +| `ingress.enabled` | Enable creation of an ingress resource. Requires, that the http service is also enabled. | `false` | +| `ingress.className` | Ingress class. | `nginx` | +| `ingress.annotations` | Additional ingress annotations. | `{}` | +| `ingress.labels` | Additional ingress labels. | `{}` | +| `ingress.hosts` | Ingress specific configuration. Specification only required when another ingress controller is used instead of `t1k. | `[]` | +| `ingress.tls` | Ingress TLS settings. Specification only required when another ingress controller is used instead of `t1k``. | `[]` | + +### Pod disruption + +| Name | Description | Value | +| --------------------- | ---------------------- | ----- | +| `podDisruptionBudget` | Pod disruption budget. | `{}` | + +### Network + +| Name | Description | Value | +| ----------------- | ------------------------------------------------------------------------------------------------------------------ | ----- | +| `networkPolicies` | Deploy network policies based on the used container network interface (CNI) implementation - like calico or weave. | `{}` | + +### Prometheus + +| Name | Description | Value | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| `prometheus.metrics.enabled` | Enable of scraping metrics by Prometheus. | `true` | +| `prometheus.metrics.podMonitor.enabled` | Enable creation of a podMonitor. Excludes the existence of a serviceMonitor resource. | `false` | +| `prometheus.metrics.podMonitor.annotations` | Additional podMonitor annotations. | `{}` | +| `prometheus.metrics.podMonitor.enableHttp2` | Enable HTTP2. | `true` | +| `prometheus.metrics.podMonitor.followRedirects` | FollowRedirects configures whether scrape requests follow HTTP 3xx redirects. | `false` | +| `prometheus.metrics.podMonitor.honorLabels` | Honor labels. | `false` | +| `prometheus.metrics.podMonitor.labels` | Additional podMonitor labels. | `{}` | +| `prometheus.metrics.podMonitor.interval` | Interval at which metrics should be scraped. If not specified Prometheus' global scrape interval is used. | `60s` | +| `prometheus.metrics.podMonitor.path` | HTTP path for scraping Prometheus metrics. | `/metrics` | +| `prometheus.metrics.podMonitor.relabelings` | RelabelConfigs to apply to samples before scraping. Prometheus Operator automatically adds relabelings for a few standard Kubernetes fields. | `[]` | +| `prometheus.metrics.podMonitor.scrapeTimeout` | Timeout after which the scrape is ended. If not specified, global Prometheus scrape timeout is used. | `30s` | +| `prometheus.metrics.podMonitor.scheme` | HTTP scheme to use for scraping. For example `http` or `https`. | `http` | +| `prometheus.metrics.podMonitor.tlsConfig` | TLS configuration to use when scraping the metric endpoint by Prometheus. | `{}` | +| `prometheus.metrics.serviceMonitor.enabled` | Enable creation of a serviceMonitor. Excludes the existence of a podMonitor resource. | `false` | +| `prometheus.metrics.serviceMonitor.annotations` | Additional serviceMonitor annotations. | `{}` | +| `prometheus.metrics.serviceMonitor.labels` | Additional serviceMonitor labels. | `{}` | +| `prometheus.metrics.serviceMonitor.enableHttp2` | Enable HTTP2. | `true` | +| `prometheus.metrics.serviceMonitor.followRedirects` | FollowRedirects configures whether scrape requests follow HTTP 3xx redirects. | `false` | +| `prometheus.metrics.serviceMonitor.honorLabels` | Honor labels. | `false` | +| `prometheus.metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. If not specified Prometheus' global scrape interval is used. | `60s` | +| `prometheus.metrics.serviceMonitor.path` | HTTP path for scraping Prometheus metrics. | `/metrics` | +| `prometheus.metrics.serviceMonitor.relabelings` | RelabelConfigs to apply to samples before scraping. Prometheus Operator automatically adds relabelings for a few standard Kubernetes fields. | `[]` | +| `prometheus.metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended. If not specified, global Prometheus scrape timeout is used. | `30s` | +| `prometheus.metrics.serviceMonitor.scheme` | HTTP scheme to use for scraping. For example `http` or `https`. | `http` | +| `prometheus.metrics.serviceMonitor.tlsConfig` | TLS configuration to use when scraping the metric endpoint by Prometheus. | `{}` | +| `prometheus.rules` | Array of Prometheus rules for monitoring the application and triggering alerts. | `[]` | + +### Service + +| Name | Description | Value | +| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `services.http.enabled` | Enable the service. | `true` | +| `services.http.annotations` | Additional service annotations. | `{}` | +| `services.http.externalIPs` | External IPs for the service. | `[]` | +| `services.http.externalTrafficPolicy` | If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster external traffic. Furthermore, this enables source IP preservation. | `Cluster` | +| `services.http.internalTrafficPolicy` | If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster internal traffic. | `Cluster` | +| `services.http.ipFamilies` | IPFamilies is list of IP families (e.g. `IPv4`, `IPv6`) assigned to this service. This field is usually assigned automatically based on cluster configuration and only required for customization. | `[]` | +| `services.http.labels` | Additional service labels. | `{}` | +| `services.http.loadBalancerClass` | LoadBalancerClass is the class of the load balancer implementation this Service belongs to. Requires service from type `LoadBalancer`. | `""` | +| `services.http.loadBalancerIP` | LoadBalancer will get created with the IP specified in this field. Requires service from type `LoadBalancer`. | `""` | +| `services.http.loadBalancerSourceRanges` | Source range filter for LoadBalancer. Requires service from type `LoadBalancer`. | `[]` | +| `services.http.port` | Port to forward the traffic to. | `9191` | +| `services.http.sessionAffinity` | Supports `ClientIP` and `None`. Enable client IP based session affinity via `ClientIP`. | `None` | +| `services.http.sessionAffinityConfig` | Contains the configuration of the session affinity. | `{}` | +| `services.http.type` | Kubernetes service type for the traffic. | `ClusterIP` | + +### ServiceAccount + +| Name | Description | Value | +| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `serviceAccount.existing.enabled` | Use an existing service account instead of creating a new one. Assumes that the user has all the necessary kubernetes API authorizations. | `false` | +| `serviceAccount.existing.serviceAccountName` | Name of the existing service account. | `""` | +| `serviceAccount.new.annotations` | Additional service account annotations. | `{}` | +| `serviceAccount.new.labels` | Additional service account labels. | `{}` | +| `serviceAccount.new.automountServiceAccountToken` | Enable/disable auto mounting of the service account token. | `true` | +| `serviceAccount.new.imagePullSecrets` | ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this serviceAccount. | `[]` | +| `serviceAccount.new.secrets` | Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. | `[]` | diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..830b80c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1880 @@ +{ + "name": "prometheus-fail2ban-exporter", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "prometheus-fail2ban-exporter", + "license": "MIT", + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdown-link-check": "^3.13.6", + "markdownlint-cli": "^0.43.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/@bitnami/readme-generator-for-helm": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.6.1.tgz", + "integrity": "sha512-rN0m0sfbOuaNdCmQWBfSj9o4kgzz+Dw67Dl1ssDVqghv/UpLkrDmNuTxhD1CWu+sesGL66UYJ2VplGz9KxlAdg==", + "dev": true, + "dependencies": { + "commander": "^7.1.0", + "dot-object": "^2.1.4", + "lodash": "^4.17.21", + "markdown-table": "^2.0.0", + "yaml": "^2.0.0-3" + }, + "bin": { + "readme-generator": "bin/index.js" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@oozcitak/dom": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", + "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/url": "1.0.4", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/infra": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", + "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@oozcitak/url": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", + "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/util": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", + "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-object": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.4.tgz", + "integrity": "sha512-7FXnyyCLFawNYJ+NhkqyP9Wd2yzuo+7n9pGiYpkmXCTYa8Ci2U0eUNDVg5OuO5Pm6aFXI2SWN8/N/w7SJWu1WA==", + "dev": true, + "dependencies": { + "commander": "^4.0.0", + "glob": "^7.1.5" + }, + "bin": { + "dot-object": "bin/dot-object" + } + }, + "node_modules/dot-object/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-link-extractor": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-link-extractor/-/html-link-extractor-1.0.5.tgz", + "integrity": "sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio": "^1.0.0-rc.10" + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", + "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-relative-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.0.0.tgz", + "integrity": "sha512-PkzoL1qKAYXNFct5IKdKRH/iBQou/oCC85QhXj6WKtUQBliZ4Yfd3Zk27RHu9KQG8r6zgvAA2AQKC9p+rqTszg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute-url": "^4.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/link-check": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.4.0.tgz", + "integrity": "sha512-0Pf4xBVUnwJdbDgpBlhHNmWDtbVjHTpIFs+JaBuIsC9PKRxjv4KMGCO2Gc8lkVnqMf9B/yaNY+9zmMlO5MyToQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-relative-url": "^4.0.0", + "ms": "^2.1.3", + "needle": "^3.3.1", + "node-email-verifier": "^2.0.0", + "proxy-agent": "^6.4.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-link-check": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.13.6.tgz", + "integrity": "sha512-JiqexKOR+oaBovJ16x/VEN886CzPI48bSGUcKJvnkHVS8xSb9fRJtsdcLwG8+5QQ/V0UZKFmW8JEZFcZbd0BBA==", + "dev": true, + "license": "ISC", + "dependencies": { + "async": "^3.2.6", + "chalk": "^5.3.0", + "commander": "^12.1.0", + "link-check": "^5.4.0", + "markdown-link-extractor": "^4.0.2", + "needle": "^3.3.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "xmlbuilder2": "^3.1.1" + }, + "bin": { + "markdown-link-check": "markdown-link-check" + } + }, + "node_modules/markdown-link-check/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/markdown-link-extractor": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-4.0.2.tgz", + "integrity": "sha512-5cUOu4Vwx1wenJgxaudsJ8xwLUMN7747yDJX3V/L7+gi3e4MsCm7w5nbrDQQy8nEfnl4r5NV3pDXMAjhGXYXAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "html-link-extractor": "^1.0.5", + "marked": "^12.0.1" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdownlint": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.36.1.tgz", + "integrity": "sha512-s73fU2CQN7WCgjhaQUQ8wYESQNzGRNOKDd+3xgVqu8kuTEhmwepd/mxOv1LR2oV046ONrTLBFsM7IoKWNvmy5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "markdown-it": "14.1.0", + "markdownlint-micromark": "0.1.12" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.43.0.tgz", + "integrity": "sha512-6vwurKK4B21eyYzwgX6ph13cZS7hE6LZfcS8QyD722CyxVD2RtAvbZK2p7k+FZbbKORulEuwl+hJaEq1l6/hoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~12.1.0", + "glob": "~11.0.0", + "ignore": "~6.0.2", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "5.0.1", + "markdownlint": "~0.36.1", + "minimatch": "~10.0.1", + "run-con": "~1.3.2", + "smol-toml": "~1.3.1" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/markdownlint-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-micromark": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.12.tgz", + "integrity": "sha512-RlB6EwMGgc0sxcIhOQ2+aq7Zw1V2fBnzbXKGgYK/mVWdT7cz34fteKSwfYeo4rL6+L/q2tyC9QtD/PgZbkdyJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-email-verifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-2.0.0.tgz", + "integrity": "sha512-AHcppjOH2KT0mxakrxFMOMjV/gOVMRpYvnJUkNfgF9oJ3INdVmqcMFJ5TlM8elpTPwt6A7bSp1IMnnWcxGom/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3", + "validator": "^13.11.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", + "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smol-toml": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", + "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, + "node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xmlbuilder2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz", + "integrity": "sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/dom": "1.15.10", + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8", + "js-yaml": "3.14.1" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/xmlbuilder2/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/xmlbuilder2/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/xmlbuilder2/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "dev": true, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b280355 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "prometheus-fail2ban-exporter", + "homepage": "https://git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter.git", + "license": "MIT", + "private": true, + "engineStrict": true, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "scripts": { + "readme:link": "markdown-link-check *.md", + "readme:lint": "markdownlint *.md -f", + "readme:parameters": "readme-generator -v values.yaml -r README.md" + }, + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdown-link-check": "^3.13.6", + "markdownlint-cli": "^0.43.0" + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..59b6314 --- /dev/null +++ b/renovate.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "assignees": [ "volker.raschek" ], + "customManagers": [ + { + "fileMatch": [ + "^Chart\\.yaml$" + ], + "matchStrings": [ + "appVersion: \"(?.*?)\"\\s+" + ], + "datasourceTemplate": "docker", + "depNameTemplate": "volker.raschek/prometheus-fail2ban-exporter", + "lookupNameTemplate": "git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter", + "versioningTemplate": "semver" + }, + { + "fileMatch": ["^README\\.md$"], + "matchStrings": [ + "VERSION=(?.*)" + ], + "depNameTemplate": "volker.raschek/prometheus-fail2ban-exporter", + "packageNameTemplate": "https://git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter", + "datasourceTemplate": "git-tags", + "versioningTemplate": "semver" + } + ], + "labels": [ "renovate" ], + "packageRules": [ + { + "addLabels": [ "renovate/automerge", "renovate/npm" ], + "automerge": true, + "matchPackageNames": [ + "markdownlint-cli", + "markdown-link-check", + "@bitnami/readme-generator-for-helm" + ], + "matchManagers": [ "npm" ], + "matchUpdateTypes": [ "minor", "patch"] + }, + { + "addLabels": [ "renovate/automerge", "renovate/container" ], + "automerge": true, + "excludePackagePatterns": [ + "volker.raschek/prometheus-fail2ban-exporter" + ], + "matchDatasources": [ + "docker" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ] + } + ], + "rebaseLabel": "renovate/rebase", + "rebaseWhen": "behind-base-branch" +} diff --git a/templates/prometheus-fail2ban-exporter/_common.tpl b/templates/prometheus-fail2ban-exporter/_common.tpl new file mode 100644 index 0000000..d488b26 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_common.tpl @@ -0,0 +1,58 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "prometheus-fail2ban-exporter.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "prometheus-fail2ban-exporter.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "prometheus-fail2ban-exporter.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common annotations +*/}} +{{- define "prometheus-fail2ban-exporter.annotations" -}} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "prometheus-fail2ban-exporter.labels" -}} +{{ include "prometheus-fail2ban-exporter.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +helm.sh/chart: {{ include "prometheus-fail2ban-exporter.chart" . }} +{{- end }} + +{{/* +Common selector labels +*/}} +{{- define "prometheus-fail2ban-exporter.selectorLabels" -}} +app.kubernetes.io/name: {{ include "prometheus-fail2ban-exporter.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/_configMap.tpl b/templates/prometheus-fail2ban-exporter/_configMap.tpl new file mode 100644 index 0000000..b531a39 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_configMap.tpl @@ -0,0 +1,20 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.configMap.grafanaDashboards.fail2banExporter.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.grafana.dashboards.fail2banExporter.annotations }} +{{ toYaml .Values.grafana.dashboards.fail2banExporter.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.configMap.grafanaDashboards.fail2banExporter.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.grafana.dashboards.fail2banExporter.labels }} +{{ toYaml .Values.grafana.dashboards.fail2banExporter.labels }} +{{- end }} +{{ toYaml .Values.grafana.dashboardDiscoveryLabels }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/_daemonSet.tpl b/templates/prometheus-fail2ban-exporter/_daemonSet.tpl new file mode 100644 index 0000000..5490b51 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_daemonSet.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.daemonSet.annotations }} +{{ toYaml .Values.daemonSet.annotations }} +{{- end }} +{{- end }} + +{{/* env */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.env" -}} +{{- $env := dict "env" (.Values.daemonSet.fail2banExporter.env | default (list) ) }} +{{- if and (hasKey .Values.daemonSet.fail2banExporter.resources "limits") (hasKey .Values.daemonSet.fail2banExporter.resources.limits "cpu") }} +{{- $env = merge $env (dict "env" (list (dict "name" "GOMAXPROCS" "valueFrom" (dict "resourceFieldRef" (dict "divisor" "1" "resource" "limits.cpu"))))) }} +{{- end }} +{{ toYaml $env }} +{{- end -}} + +{{/* image */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.images.fail2ban-exporter.fqin" -}} +{{- $registry := .Values.daemonSet.fail2banExporter.image.registry -}} +{{- $repository := .Values.daemonSet.fail2banExporter.image.repository -}} +{{- $tag := default .Chart.AppVersion .Values.daemonSet.fail2banExporter.image.tag -}} +{{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- end -}} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.daemonSet.labels }} +{{ toYaml .Values.daemonSet.labels }} +{{- end }} +{{- end }} + +{{/* serviceAccount */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.serviceAccount" -}} +{{- if .Values.serviceAccount.existing.enabled -}} +{{- printf "%s" .Values.serviceAccount.existing.serviceAccountName -}} +{{- else -}} +{{- include "prometheus-fail2ban-exporter.fullname" . -}} +{{- end -}} +{{- end }} + +{{/* volumeMounts */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.volumeMounts" -}} +{{- $volumeMounts := .Values.daemonSet.fail2banExporter.volumeMounts | default list }} +{{- $volumeMounts = concat $volumeMounts (list (dict "name" "config-d" "mountPath" "/etc/prometheus-fail2ban-exporter/config.d" )) }} +{{ toYaml (dict "volumeMounts" $volumeMounts) }} +{{- end -}} + +{{/* volumes */}} + +{{- define "prometheus-fail2ban-exporter.daemonSet.volumes" -}} +{{- $volumes := .Values.daemonSet.volumes | default list }} + +{{- $webConfigSecretName := .Values.config.webConfig.existingSecret.secretName -}} +{{- if not .Values.config.webConfig.existingSecret.enabled }} +{{- $webConfigSecretName = printf "%s-web-config" (include "prometheus-fail2ban-exporter.fullname" . ) }} +{{- end }} + +{{- $volumes = concat $volumes (list (dict "name" "config-d" "secret" (dict "secretName" $webConfigSecretName))) }} + +{{ toYaml (dict "volumes" $volumes) }} + +{{- end -}} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/_ingress.tpl b/templates/prometheus-fail2ban-exporter/_ingress.tpl new file mode 100644 index 0000000..c643a51 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_ingress.tpl @@ -0,0 +1,19 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.ingress.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.ingress.annotations }} +{{ toYaml .Values.ingress.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.ingress.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.ingress.labels }} +{{ toYaml .Values.ingress.labels }} +{{- end }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/_pod.tpl b/templates/prometheus-fail2ban-exporter/_pod.tpl new file mode 100644 index 0000000..50b2c0b --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_pod.tpl @@ -0,0 +1,17 @@ +--- + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.pod.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.pod.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- end }} + +{{- define "prometheus-fail2ban-exporter.pod.selectorLabels" -}} +{{ include "prometheus-fail2ban-exporter.selectorLabels" . }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/_podMonitors.tpl b/templates/prometheus-fail2ban-exporter/_podMonitors.tpl new file mode 100644 index 0000000..103b143 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_podMonitors.tpl @@ -0,0 +1,19 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.podMonitors.http.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.prometheus.metrics.podMonitor.annotations }} +{{ toYaml .Values.prometheus.metrics.podMonitor.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.podMonitors.http.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.prometheus.metrics.podMonitor.labels }} +{{ toYaml .Values.prometheus.metrics.podMonitor.labels }} +{{- end }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/_prometheusRules.tpl b/templates/prometheus-fail2ban-exporter/_prometheusRules.tpl new file mode 100644 index 0000000..f3b8ab4 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_prometheusRules.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.prometheusRules.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.prometheusRules.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/_secrets.tpl b/templates/prometheus-fail2ban-exporter/_secrets.tpl new file mode 100644 index 0000000..eefefd6 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_secrets.tpl @@ -0,0 +1,19 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.secrets.webConfig.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.config.webConfig.secret.annotations }} +{{ toYaml .Values.config.webConfig.secret.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.secrets.webConfig.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.config.webConfig.secret.labels }} +{{ toYaml .Values.config.webConfig.secret.labels }} +{{- end }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/_serviceAccount.tpl b/templates/prometheus-fail2ban-exporter/_serviceAccount.tpl new file mode 100644 index 0000000..6b6c9ea --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_serviceAccount.tpl @@ -0,0 +1,17 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.serviceAccount.annotations" -}} +{{- if .Values.serviceAccount.new.annotations }} +{{ toYaml .Values.serviceAccount.new.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.serviceAccount.labels" -}} +{{- if .Values.serviceAccount.new.labels }} +{{ toYaml .Values.serviceAccount.new.labels }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/_serviceMonitors.tpl b/templates/prometheus-fail2ban-exporter/_serviceMonitors.tpl new file mode 100644 index 0000000..a03013c --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_serviceMonitors.tpl @@ -0,0 +1,25 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.serviceMonitors.http.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.prometheus.metrics.serviceMonitor.annotations }} +{{ toYaml .Values.prometheus.metrics.serviceMonitor.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.serviceMonitors.http.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{- if .Values.prometheus.metrics.serviceMonitor.labels }} +{{ toYaml .Values.prometheus.metrics.serviceMonitor.labels }} +{{- end }} +{{- end }} + +{{- define "prometheus-fail2ban-exporter.serviceMonitors.http.selectorLabels" -}} +{{ include "prometheus-fail2ban-exporter.selectorLabels" . }} +{{/* Add label to select the correct service via `selector.matchLabels` of the serviceMonitor resource. */}} +app.kubernetes.io/service-name: http +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/_services.tpl b/templates/prometheus-fail2ban-exporter/_services.tpl new file mode 100644 index 0000000..8b353e2 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/_services.tpl @@ -0,0 +1,29 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "prometheus-fail2ban-exporter.services.http.annotations" -}} +{{ include "prometheus-fail2ban-exporter.annotations" . }} +{{- if .Values.services.http.annotations }} +{{ toYaml .Values.services.http.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "prometheus-fail2ban-exporter.services.http.labels" -}} +{{ include "prometheus-fail2ban-exporter.labels" . }} +{{/* Add label to select the correct service via `selector.matchLabels` of the serviceMonitor resource. */}} +app.kubernetes.io/service-name: http +{{- if .Values.services.http.labels }} +{{ toYaml .Values.services.http.labels }} +{{- end }} +{{- end }} + +{{/* names */}} + +{{- define "prometheus-fail2ban-exporter.services.http.name" -}} +{{- if .Values.services.http.enabled -}} +{{ include "prometheus-fail2ban-exporter.fullname" . }}-http +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/configMapGrafanaDashboardFail2BanExporter.yaml b/templates/prometheus-fail2ban-exporter/configMapGrafanaDashboardFail2BanExporter.yaml new file mode 100644 index 0000000..0b51c70 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/configMapGrafanaDashboardFail2BanExporter.yaml @@ -0,0 +1,895 @@ +{{- if and .Values.grafana.enabled .Values.grafana.dashboards.fail2banExporter }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + {{- with (include "prometheus-fail2ban-exporter.configMap.grafanaDashboards.fail2banExporter.annotations" . | fromYaml) }} + annotations: + {{- tpl (. | toYaml) $ | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.configMap.grafanaDashboards.fail2banExporter.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }}-grafana-dashboard-fail2ban-exporter + namespace: {{ .Release.Namespace }} +data: + fail2banExporter.json: |- + {{`{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.1.8" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 2, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": ".*Time" + }, + "properties": [ + { + "id": "unit", + "value": "s" + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 206, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.1.8", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "f2b_config_jail_max_retries{instance=~\"$instance\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{jail}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "f2b_config_jail_ban_time{instance=~\"$instance\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "{{jail}}", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "f2b_config_jail_find_time{instance=~\"$instance\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "{{jail}}", + "refId": "C" + } + ], + "title": "F2B Config", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #A": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #B": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #C": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "instance": { + "aggregations": [], + "operation": "groupby" + }, + "jail": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value #A (lastNotNull)": "Max Retries", + "Value #B (lastNotNull)": "Ban Time", + "Value #C (lastNotNull)": "Find Time", + "jail": "Jail" + } + } + } + ], + "transparent": true, + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 190, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_jail_failed_total{instance=~\"$instance\"}", + "hide": false, + "interval": "", + "legendFormat": "{{jail}} ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Failures (Total)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 191, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_jail_banned_total{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "{{jail}} ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Bans (Total)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 208, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_jail_failed_current{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "{{jail}} ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Failures (Current)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 209, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_jail_banned_current{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "{{jail}} ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Bans (Current)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 203, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_up{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "Up ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Up", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 204, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": true, + "expr": "f2b_errors{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "{{type}} ({{instance}})", + "range": true, + "refId": "A" + } + ], + "title": "Fail2Ban Exporter Errors", + "transparent": true, + "type": "timeseries" + } + ], + "refresh": "30s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "Data Source", + "multi": false, + "name": "DataSource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "definition": "f2b_up", + "description": "Select which instance(s) to show", + "hide": 0, + "includeAll": false, + "label": "Instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "f2b_up", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/.*instance=\"([^\"]+)\"/", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "F2B", + "uid": "cTkH9AT7z", + "version": 3, + "weekStart": "" + }`}} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/daemonSet.yaml b/templates/prometheus-fail2ban-exporter/daemonSet.yaml new file mode 100644 index 0000000..7814335 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/daemonSet.yaml @@ -0,0 +1,129 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + {{- with (include "prometheus-fail2ban-exporter.daemonSet.annotations" . | fromYaml) }} + annotations: + {{- tpl (. | toYaml) $ | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.daemonSet.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "prometheus-fail2ban-exporter.pod.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "prometheus-fail2ban-exporter.pod.labels" . | nindent 8 }} + spec: + {{- with .Values.daemonSet.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: fail2ban-exporter + args: + # - "--web.config.file=/etc/prometheus-fail2ban-exporter/config.d/webConfig.yaml" + - "--web.listen-address=:9191" + {{- range .Values.daemonSet.fail2banExporter.args }} + - {{ . | quote }} + {{- end }} + {{- $env := (include "prometheus-fail2ban-exporter.daemonSet.env" . | fromYaml) }} + {{- if and (hasKey $env "env") (gt (len $env.env) 0) }} + env: + {{- toYaml $env.env | nindent 8 }} + {{- end }} + {{- with .Values.daemonSet.fail2banExporter.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + image: {{ include "prometheus-fail2ban-exporter.daemonSet.images.fail2ban-exporter.fqin" . | quote }} + imagePullPolicy: {{ .Values.daemonSet.fail2banExporter.image.pullPolicy }} + livenessProbe: + tcpSocket: + port: 9191 + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 3 + readinessProbe: + tcpSocket: + port: 9191 + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + ports: + - name: http + containerPort: 9191 + protocol: TCP + {{- with .Values.daemonSet.fail2banExporter.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.daemonSet.fail2banExporter.securityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- $volumeMounts := (include "prometheus-fail2ban-exporter.daemonSet.volumeMounts" . | fromYaml) }} + {{- if hasKey $volumeMounts "volumeMounts" }} + volumeMounts: + {{- toYaml $volumeMounts.volumeMounts | nindent 8 }} + {{- end }} + {{- with .Values.daemonSet.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.daemonSet.dnsPolicy }} + dnsPolicy: {{ .Values.daemonSet.dnsPolicy }} + {{- end }} + {{- if .Values.daemonSet.hostname }} + hostname: {{ .Values.daemonSet.hostname }} + {{- end }} + hostNetwork: {{ .Values.daemonSet.hostNetwork }} + {{- with .Values.daemonSet.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.daemonSet.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.daemonSet.priorityClassName }} + priorityClassName: {{ .Values.daemonSet.priorityClassName }} + {{- end }} + {{- if .Values.daemonSet.restartPolicy }} + restartPolicy: {{ .Values.daemonSet.restartPolicy }} + {{- end }} + {{- with .Values.daemonSet.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccount: {{ include "prometheus-fail2ban-exporter.daemonSet.serviceAccount" . }} + {{- if .Values.daemonSet.subdomain }} + subdomain: {{ .Values.daemonSet.subdomain }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.daemonSet.terminationGracePeriodSeconds }} + {{- with .Values.daemonSet.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.daemonSet.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- $volumes := (include "prometheus-fail2ban-exporter.daemonSet.volumes" . | fromYaml) }} + {{- if hasKey $volumes "volumes" }} + volumes: + {{- toYaml $volumes.volumes | nindent 6 }} + {{- end }} + {{- with .Values.daemonSet.updateStrategy }} + updateStrategy: + {{- toYaml . | nindent 4 }} + {{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/ingress.yaml b/templates/prometheus-fail2ban-exporter/ingress.yaml new file mode 100644 index 0000000..4e3318d --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/ingress.yaml @@ -0,0 +1,45 @@ +{{- if and .Values.services.http.enabled .Values.ingress.enabled }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + {{- with (include "prometheus-fail2ban-exporter.ingress.annotations" . | fromYaml) }} + annotations: + {{- tpl (. | toYaml) $ | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.ingress.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + ingressClassName: {{ .Values.ingress.className }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ tpl .host $ | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if .pathType }} + pathType: {{ .pathType }} + {{- end }} + backend: + service: + name: {{ include "prometheus-fail2ban-exporter.fullname" $ }} + port: + number: {{ $.Values.services.http.port }} + {{- end }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ tpl . $ | quote }} + {{- end }} + secretName: {{ .secretName | quote }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/podMonitor.yaml b/templates/prometheus-fail2ban-exporter/podMonitor.yaml new file mode 100644 index 0000000..8428078 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/podMonitor.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.prometheus.metrics.enabled .Values.prometheus.metrics.podMonitor.enabled (not .Values.prometheus.metrics.serviceMonitor.enabled) }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + {{- with (include "prometheus-fail2ban-exporter.podMonitors.http.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.podMonitors.http.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }}-http + namespace: {{ .Release.Namespace }} +spec: + podMetricsEndpoints: + - enableHttp2: {{ required "The enableHttp2 option of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.enableHttp2 }} + followRedirects: {{ required "The followRedirects option of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.followRedirects }} + honorLabels: {{ required "The honorLabels option of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.honorLabels }} + interval: {{ required "The scrape interval of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.interval }} + path: {{ required "The metric path of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.path }} + port: "http" + {{- with .Values.prometheus.metrics.podMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + scrapeTimeout: {{ required "The scrape timeout of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.scrapeTimeout }} + scheme: {{ required "The scheme of the podMonitor is not defined!" .Values.prometheus.metrics.podMonitor.scheme}} + {{- with .Values.prometheus.metrics.podMonitor.tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 6 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "prometheus-fail2ban-exporter.pod.selectorLabels" . | nindent 6 }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/prometheusRules.yaml b/templates/prometheus-fail2ban-exporter/prometheusRules.yaml new file mode 100644 index 0000000..2ef88a4 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/prometheusRules.yaml @@ -0,0 +1,23 @@ +{{- if gt (len .Values.prometheus.rules) 0 }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + {{- with (include "prometheus-fail2ban-exporter.prometheusRules.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.prometheusRules.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: +{{- with .Values.prometheus.rules }} + groups: + - name: {{ template "prometheus-fail2ban-exporter.fullname" $ }} + rules: + {{ toYaml . | nindent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/secretWebConfig.yaml b/templates/prometheus-fail2ban-exporter/secretWebConfig.yaml new file mode 100644 index 0000000..2d46b9a --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/secretWebConfig.yaml @@ -0,0 +1,19 @@ +{{- if not .Values.config.webConfig.existingSecret.enabled }} +--- +apiVersion: v1 +kind: Secret +metadata: + {{- with (include "prometheus-fail2ban-exporter.secrets.webConfig.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.secrets.webConfig.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }}-web-config + namespace: {{ .Release.Namespace }} +stringData: + webConfig.yaml: | + {{- toYaml .Values.config.webConfig.secret.webConfig | nindent 4 }} +{{- end }} diff --git a/templates/prometheus-fail2ban-exporter/serviceAccount.yaml b/templates/prometheus-fail2ban-exporter/serviceAccount.yaml new file mode 100644 index 0000000..0b45579 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/serviceAccount.yaml @@ -0,0 +1,25 @@ +{{- if not .Values.serviceAccount.existing.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "prometheus-fail2ban-exporter.serviceAccount.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.serviceAccount.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.serviceAccount.new.automountServiceAccountToken }} +{{- with .Values.serviceAccount.new.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- with .Values.serviceAccount.new.secrets }} +secrets: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/serviceHTTP.yaml b/templates/prometheus-fail2ban-exporter/serviceHTTP.yaml new file mode 100644 index 0000000..b77c887 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/serviceHTTP.yaml @@ -0,0 +1,57 @@ +{{- if .Values.services.http.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + {{- with (include "prometheus-fail2ban-exporter.services.http.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.services.http.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.services.http.name" . }} + namespace: {{ .Release.Namespace }} +spec: + {{- if not (empty .Values.services.http.externalIPs) }} + externalIPs: + {{- range .Values.services.http.externalIPs }} + - {{ . }} + {{- end }} + {{- end }} + {{- if and (or (eq .Values.services.http.type "LoadBalancer") (eq .Values.services.http.type "NodePort") ) .Values.services.http.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.services.http.externalTrafficPolicy }} + {{- end }} + internalTrafficPolicy: {{ required "No internal traffic policy defined!" .Values.services.http.internalTrafficPolicy }} + {{- if .Values.services.http.ipFamilies }} + ipFamilies: + {{- range .Values.services.http.ipFamilies }} + - {{ . }} + {{- end }} + {{- end }} + {{- if and (eq .Values.services.http.type "LoadBalancer") .Values.services.http.loadBalancerClass }} + loadBalancerClass: {{ .Values.services.http.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.services.http.type "LoadBalancer") .Values.services.http.loadBalancerIP }} + loadBalancerIP: {{ .Values.services.http.loadBalancerIP }} + {{- end }} + {{- if eq .Values.services.http.type "LoadBalancer" }} + loadBalancerSourceRanges: + {{- range .Values.services.http.loadBalancerSourceRanges }} + - {{ . }} + {{- end }} + {{- end }} + ports: + - name: http + protocol: TCP + port: {{ required "No service port defined!" .Values.services.http.port }} + selector: + {{- include "prometheus-fail2ban-exporter.pod.selectorLabels" . | nindent 4 }} + sessionAffinity: {{ required "No session affinity defined!" .Values.services.http.sessionAffinity }} + {{- with .Values.services.http.sessionAffinityConfig }} + sessionAffinityConfig: + {{- toYaml . | nindent 4}} + {{- end }} + type: {{ required "No service type defined!" .Values.services.http.type }} +{{- end }} \ No newline at end of file diff --git a/templates/prometheus-fail2ban-exporter/serviceMonitorHTTP.yaml b/templates/prometheus-fail2ban-exporter/serviceMonitorHTTP.yaml new file mode 100644 index 0000000..be32230 --- /dev/null +++ b/templates/prometheus-fail2ban-exporter/serviceMonitorHTTP.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.services.http.enabled .Values.prometheus.metrics.enabled .Values.prometheus.metrics.serviceMonitor.enabled (not .Values.prometheus.metrics.podMonitor.enabled)}} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + {{- with (include "prometheus-fail2ban-exporter.serviceMonitors.http.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "prometheus-fail2ban-exporter.serviceMonitors.http.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-fail2ban-exporter.fullname" . }}-http + namespace: {{ .Release.Namespace }} +spec: + endpoints: + - enableHttp2: {{ required "The enableHttp2 option of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.enableHttp2 }} + followRedirects: {{ required "The followRedirects option of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.followRedirects }} + honorLabels: {{ required "The honorLabels option of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.honorLabels }} + interval: {{ required "The scrape interval of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.interval }} + path: {{ required "The metric path of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.path }} + {{- with .Values.prometheus.metrics.serviceMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + scrapeTimeout: {{ required "The scrape timeout of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.scrapeTimeout }} + scheme: {{ required "The scheme of the serviceMonitor is not defined!" .Values.prometheus.metrics.serviceMonitor.scheme }} + targetPort: {{ required "The port of the service is not defined!" .Values.services.http.port }} + {{- with .Values.prometheus.metrics.serviceMonitor.tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 6 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "prometheus-fail2ban-exporter.serviceMonitors.http.selectorLabels" . | nindent 6 }} +{{- end }} \ No newline at end of file diff --git a/unittests/configMaps/grafanaDashboardPostgresExporter.yaml b/unittests/configMaps/grafanaDashboardPostgresExporter.yaml new file mode 100644 index 0000000..e107155 --- /dev/null +++ b/unittests/configMaps/grafanaDashboardPostgresExporter.yaml @@ -0,0 +1,79 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: ConfigMap template (Grafana Dashboard PostgresExporter) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/configMapGrafanaDashboardFail2BanExporter.yaml +tests: +- it: Rendering fail2banExporter + asserts: + - hasDocuments: + count: 0 + +- it: Rendering + set: + grafana.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: ConfigMap + name: prometheus-fail2ban-exporter-unittest-grafana-dashboard-fail2ban-exporter + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + grafana_dashboard: "1" + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - exists: + path: data["fail2banExporter.json"] + +- it: Test custom annotations and labels + set: + grafana.enabled: true + grafana.dashboards.fail2banExporter.annotations: + foo: bar + grafana.dashboards.fail2banExporter.labels: + bar: foo + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + grafana_dashboard: "1" + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + bar: foo + +- it: Test custom grafana discovery labels + set: + grafana.enabled: true + grafana.dashboardDiscoveryLabels: + grafana_dashboard: null + my-custom-discovery-label: my-value + asserts: + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + my-custom-discovery-label: my-value + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 diff --git a/unittests/daemonset/daemonset.yaml b/unittests/daemonset/daemonset.yaml new file mode 100644 index 0000000..51c9412 --- /dev/null +++ b/unittests/daemonset/daemonset.yaml @@ -0,0 +1,377 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: DaemonSet template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/daemonSet.yaml +tests: +- it: Rendering default + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: apps/v1 + kind: DaemonSet + name: prometheus-fail2ban-exporter-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - notExists: + path: spec.template.spec.affinity + - notExists: + path: spec.template.spec.containers[0].envFrom + - equal: + path: spec.template.spec.containers[0].args + value: + # - --web.config.file=/etc/prometheus-fail2ban-exporter/config.d/webConfig.yaml + - --web.listen-address=:9191 + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - mountPath: /var/run/fail2ban + name: socket + - mountPath: /etc/prometheus-fail2ban-exporter/config.d + name: config-d + - equal: + path: spec.template.spec.volumes + value: + - hostPath: + path: /var/run/fail2ban + type: Directory + name: socket + - name: config-d + secret: + secretName: prometheus-fail2ban-exporter-unittest-web-config + - equal: + path: spec.template.spec.containers[0].image + value: git.cryptic.systems/volker.raschek/prometheus-fail2ban-exporter:0.1.0 + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: IfNotPresent + - notExists: + path: spec.template.spec.containers[0].resources + - notExists: + path: spec.template.spec.containers[0].securityContext + - notExists: + path: spec.template.spec.dnsConfig + - notExists: + path: spec.template.spec.dnsPolicy + - notExists: + path: spec.template.spec.hostname + - equal: + path: spec.template.spec.hostNetwork + value: false + - notExists: + path: spec.template.spec.imagePullSecrets + - notExists: + path: spec.template.spec.nodeSelector + - notExists: + path: spec.template.spec.priorityClassName + - notExists: + path: spec.template.spec.restartPolicy + - notExists: + path: spec.template.spec.subdomain + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 60 + - notExists: + path: spec.template.spec.tolerations + - notExists: + path: spec.template.spec.topologySpreadConstraints + - equal: + path: spec.updateStrategy + value: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: "RollingUpdate" + +- it: Test custom affinity + set: + daemonSet.affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + asserts: + - equal: + path: spec.template.spec.affinity + value: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + +- it: Test additional arguments + set: + daemonSet.fail2banExporter.args: + - "--foo=bar" + - "--bar=foo" + asserts: + - equal: + path: spec.template.spec.containers[0].args + value: + # - --web.config.file=/etc/prometheus-fail2ban-exporter/config.d/webConfig.yaml + - --web.listen-address=:9191 + - --foo=bar + - --bar=foo + +- it: Test custom imageRegistry and imageRepository + set: + daemonSet.fail2banExporter.image.registry: registry.example.local + daemonSet.fail2banExporter.image.repository: path/special/prometheus-fail2ban-exporter + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: registry.example.local/path/special/prometheus-fail2ban-exporter:0.1.0 + +- it: Test custom imagePullPolicy + set: + daemonSet.fail2banExporter.image.pullPolicy: Always + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + +- it: Test config.webConfig.existingSecret + set: + config.webConfig.existingSecret.enabled: true + config.webConfig.existingSecret.secretName: web-config-secret + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - mountPath: /var/run/fail2ban + name: socket + - mountPath: /etc/prometheus-fail2ban-exporter/config.d + name: config-d + - equal: + path: spec.template.spec.volumes + value: + - hostPath: + path: /var/run/fail2ban + type: Directory + name: socket + - name: config-d + secret: + secretName: web-config-secret + +- it: Test custom resource limits and requests + set: + daemonSet.fail2banExporter.resources: + limits: + cpu: 100m + memory: 250MB + requests: + cpu: 25m + memory: 100MB + asserts: + - equal: + path: spec.template.spec.containers[0].env + value: + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + divisor: "1" + resource: limits.cpu + - equal: + path: spec.template.spec.containers[0].resources + value: + limits: + cpu: 100m + memory: 250MB + requests: + cpu: 25m + memory: 100MB + +- it: Test custom securityContext + set: + daemonSet.fail2banExporter.securityContext: + capabilities: + add: + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + capabilities: + add: + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +- it: Test dnsConfig + set: + daemonSet.dnsConfig: + nameservers: + - "8.8.8.8" + - "8.8.4.4" + asserts: + - equal: + path: spec.template.spec.dnsConfig + value: + nameservers: + - "8.8.8.8" + - "8.8.4.4" + +- it: Test dnsPolicy + set: + daemonSet.dnsPolicy: ClusterFirst + asserts: + - equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + +- it: Test hostNetwork, hostname, subdomain + set: + daemonSet.hostNetwork: true + daemonSet.hostname: pg-exporter + daemonSet.subdomain: exporters.internal + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: true + - equal: + path: spec.template.spec.hostname + value: pg-exporter + - equal: + path: spec.template.spec.subdomain + value: exporters.internal + +- it: Test imagePullSecrets + set: + daemonSet.imagePullSecrets: + - name: my-pull-secret + - name: my-special-secret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets + value: + - name: my-pull-secret + - name: my-special-secret + +- it: Test nodeSelector + set: + daemonSet.nodeSelector: + foo: bar + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + foo: bar + +- it: Test priorityClassName + set: + daemonSet.priorityClassName: my-priority + asserts: + - equal: + path: spec.template.spec.priorityClassName + value: my-priority + +- it: Test restartPolicy + set: + daemonSet.restartPolicy: Always + asserts: + - equal: + path: spec.template.spec.restartPolicy + value: Always + +- it: Test terminationGracePeriodSeconds + set: + daemonSet.terminationGracePeriodSeconds: 120 + asserts: + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 120 + +- it: Test tolerations + set: + daemonSet.tolerations: + - key: database/type + operator: Equal + value: fail2ban + effect: NoSchedule + asserts: + - equal: + path: spec.template.spec.tolerations + value: + - key: database/type + operator: Equal + value: fail2ban + effect: NoSchedule + +- it: Test topologySpreadConstraints + set: + daemonSet.topologySpreadConstraints: + - topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/instance: prometheus-fail2ban-exporter + asserts: + - equal: + path: spec.template.spec.topologySpreadConstraints + value: + - topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/instance: prometheus-fail2ban-exporter + +- it: Test additional volumeMounts and volumes + set: + daemonSet.fail2banExporter.volumeMounts: + - name: data + mountPath: /usr/lib/prometheus-fail2ban-exporter/data + daemonSet.volumes: + - name: data + hostPath: + path: /usr/lib/prometheus-fail2ban-exporter/data + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: data + mountPath: /usr/lib/prometheus-fail2ban-exporter/data + - name: config-d + mountPath: /etc/prometheus-fail2ban-exporter/config.d + - equal: + path: spec.template.spec.volumes + value: + - name: data + hostPath: + path: /usr/lib/prometheus-fail2ban-exporter/data + - name: config-d + secret: + secretName: prometheus-fail2ban-exporter-unittest-web-config \ No newline at end of file diff --git a/unittests/ingress/ingress.yaml b/unittests/ingress/ingress.yaml new file mode 100644 index 0000000..875f8ca --- /dev/null +++ b/unittests/ingress/ingress.yaml @@ -0,0 +1,140 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Ingress template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/ingress.yaml +tests: +- it: Skip ingress by default. + asserts: + - hasDocuments: + count: 0 + +- it: Skip ingress, when service is disabled. + set: + services.http.enabled: false + ingress.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Render ingress with default values. + set: + ingress.enabled: true + ingress.hosts: + - host: fail2ban-exporter.example.local + paths: + - path: / + pathType: Prefix + ingress.tls: + - secretName: fail2ban-exporter-http-tls + hosts: + - fail2ban-exporter.example.local + + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: networking.k8s.io/v1 + kind: Ingress + name: prometheus-fail2ban-exporter-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - equal: + path: spec.ingressClassName + value: nginx + - contains: + path: spec.rules + content: + host: fail2ban-exporter.example.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: prometheus-fail2ban-exporter-unittest + port: + number: 9191 + - contains: + path: spec.tls + content: + hosts: + - fail2ban-exporter.example.local + secretName: fail2ban-exporter-http-tls + +- it: Render ingress with custom values. + set: + ingress.enabled: true + ingress.annotations: + foo: bar + ingress.className: nginx + ingress.labels: + bar: foo + ingress.hosts: + - host: fail2ban-exporter.example.local + paths: + - path: / + pathType: Prefix + ingress.tls: + - secretName: fail2ban-exporter-http-tls + hosts: + - fail2ban-exporter.example.local + + services.http.port: 8080 + + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: networking.k8s.io/v1 + kind: Ingress + name: prometheus-fail2ban-exporter-unittest + namespace: testing + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + bar: foo + - equal: + path: spec.ingressClassName + value: nginx + - contains: + path: spec.rules + content: + host: fail2ban-exporter.example.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: prometheus-fail2ban-exporter-unittest + port: + number: 8080 + - contains: + path: spec.tls + content: + hosts: + - fail2ban-exporter.example.local + secretName: fail2ban-exporter-http-tls diff --git a/unittests/podMonitors/podMonitorHTTP.yaml b/unittests/podMonitors/podMonitorHTTP.yaml new file mode 100644 index 0000000..0f75d55 --- /dev/null +++ b/unittests/podMonitors/podMonitorHTTP.yaml @@ -0,0 +1,169 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: PodMonitor http template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/podMonitor.yaml +tests: +- it: Skip podMonitor when metrics are disabled. + set: + prometheus.metrics.enabled: false + prometheus.metrics.podMonitor.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Skip podMonitor when podMonitor is disabled. + set: + prometheus.metrics.enabled: true + prometheus.metrics.podMonitor.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Skip podMonitor when both monitor types are enabled. + set: + prometheus.metrics.enabled: true + prometheus.metrics.podMonitor.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Rendering podMonitor with default values - enabled manually. + set: + prometheus.metrics.enabled: true + prometheus.metrics.podMonitor.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: monitoring.coreos.com/v1 + kind: PodMonitor + name: prometheus-fail2ban-exporter-unittest-http + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - equal: + path: spec.podMetricsEndpoints[0].enableHttp2 + value: true + - equal: + path: spec.podMetricsEndpoints[0].followRedirects + value: false + - equal: + path: spec.podMetricsEndpoints[0].honorLabels + value: false + - equal: + path: spec.podMetricsEndpoints[0].interval + value: 60s + - equal: + path: spec.podMetricsEndpoints[0].path + value: /metrics + - equal: + path: spec.podMetricsEndpoints[0].port + value: http + - notExists: + path: spec.podMetricsEndpoints[0].relabelings + - equal: + path: spec.podMetricsEndpoints[0].scrapeTimeout + value: 30s + - equal: + path: spec.podMetricsEndpoints[0].scheme + value: http + - contains: + path: spec.namespaceSelector.matchNames + content: + testing + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/name: prometheus-fail2ban-exporter + +- it: Render podMonitor with custom annotations and labels. + set: + prometheus.metrics.enabled: true + prometheus.metrics.podMonitor.enabled: true + prometheus.metrics.podMonitor.annotations: + foo: bar + prometheus.metrics.podMonitor.labels: + bar: foo + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + bar: foo + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + +- it: Change defaults + set: + prometheus.metrics.enabled: true + prometheus.metrics.podMonitor.enabled: true + prometheus.metrics.podMonitor.enableHttp2: false + prometheus.metrics.podMonitor.followRedirects: true + prometheus.metrics.podMonitor.honorLabels: true + prometheus.metrics.podMonitor.interval: "180s" + prometheus.metrics.podMonitor.path: "/my-metrics" + prometheus.metrics.podMonitor.relabelings: + - sourceLabels: [ container ] + separator: ";" + regex: "app" + replacement: "$1" + action: "drop" + prometheus.metrics.podMonitor.scrapeTimeout: "5s" + prometheus.metrics.podMonitor.scheme: "http" + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.podMetricsEndpoints[0].enableHttp2 + value: false + - equal: + path: spec.podMetricsEndpoints[0].followRedirects + value: true + - equal: + path: spec.podMetricsEndpoints[0].honorLabels + value: true + - equal: + path: spec.podMetricsEndpoints[0].interval + value: 180s + - equal: + path: spec.podMetricsEndpoints[0].path + value: /my-metrics + - equal: + path: spec.podMetricsEndpoints[0].port + value: http + - contains: + path: spec.podMetricsEndpoints[0].relabelings + content: + sourceLabels: [ container ] + separator: ";" + regex: "app" + replacement: "$1" + action: "drop" + - equal: + path: spec.podMetricsEndpoints[0].scrapeTimeout + value: 5s + - equal: + path: spec.podMetricsEndpoints[0].scheme + value: http \ No newline at end of file diff --git a/unittests/secrets/webconfig.yaml b/unittests/secrets/webconfig.yaml new file mode 100644 index 0000000..c4d4989 --- /dev/null +++ b/unittests/secrets/webconfig.yaml @@ -0,0 +1,74 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Secret database template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/secretWebConfig.yaml +tests: +- it: Rendering default secret. + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: Secret + name: prometheus-fail2ban-exporter-unittest-web-config + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - equal: + path: stringData["webConfig.yaml"] + value: | + {} + +- it: Rendering custom webconfig. + set: + config.webConfig.secret.webConfig: + tls_server_config: + cert_file: /path/to/cert.pem + client_ca_file: /path/to/ca.pem + key_file: /path/to/key.pem + asserts: + - equal: + path: stringData["webConfig.yaml"] + value: | + tls_server_config: + cert_file: /path/to/cert.pem + client_ca_file: /path/to/ca.pem + key_file: /path/to/key.pem + +- it: Rendering custom annotations and labels. + set: + config.webConfig.secret.annotations: + foo: bar + bar: foo + config.webConfig.secret.labels: + foo: bar + bar: foo + config.webConfig.secret.webConfig: + tls_server_config: + cert_file: /path/to/cert.pem + key_file: /path/to/key.pem + client_ca_file: /path/to/ca.pem + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + bar: foo + - isSubset: + path: metadata.labels + content: + foo: bar + bar: foo \ No newline at end of file diff --git a/unittests/serviceAccounts/serviceAccount.yaml b/unittests/serviceAccounts/serviceAccount.yaml new file mode 100644 index 0000000..203c0b9 --- /dev/null +++ b/unittests/serviceAccounts/serviceAccount.yaml @@ -0,0 +1,79 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: ServiceAccount prometheus-fail2ban-exporter template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/serviceAccount.yaml +tests: +- it: Skip rendering. + set: + serviceAccount.existing.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Rendering serviceAccount with default values. + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: ServiceAccount + name: prometheus-fail2ban-exporter-unittest + namespace: testing + - notExists: + path: metadata.annotations + - notExists: + path: metadata.labels + - equal: + path: automountServiceAccountToken + value: true + - notExists: + path: imagePullSecrets + - notExists: + path: secrets + + +- it: Rendering serviceAccount with custom values. + set: + serviceAccount.new.annotations: + foo: bar + serviceAccount.new.labels: + bar: foo + serviceAccount.new.automountServiceAccountToken: false + serviceAccount.new.imagePullSecrets: + - name: "my-pull-secret" + serviceAccount.new.secrets: + - name: "my-secret" + namespace: "my-namespace" + fieldPath: "my-path" + asserts: + - hasDocuments: + count: 1 + - exists: + path: metadata.annotations + value: + foo: bar + - exists: + path: metadata.labels + value: + bar: foo + - equal: + path: metadata.name + value: prometheus-fail2ban-exporter-unittest + - equal: + path: automountServiceAccountToken + value: false + - equal: + path: imagePullSecrets + value: + - name: "my-pull-secret" + - equal: + path: secrets + value: + - name: "my-secret" + namespace: "my-namespace" + fieldPath: "my-path" diff --git a/unittests/serviceMonitors/serviceMonitorHTTP.yaml b/unittests/serviceMonitors/serviceMonitorHTTP.yaml new file mode 100644 index 0000000..9be89c5 --- /dev/null +++ b/unittests/serviceMonitors/serviceMonitorHTTP.yaml @@ -0,0 +1,168 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: ServiceMonitor http template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/serviceMonitorHTTP.yaml +tests: +- it: Skip serviceMonitor when service is disabled. + set: + prometheus.metrics.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + services.http.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Skip serviceMonitor when metrics are disabled. + set: + prometheus.metrics.enabled: false + prometheus.metrics.serviceMonitor.enabled: true + services.http.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Skip serviceMonitor when serviceMonitor is disabled. + set: + prometheus.metrics.enabled: true + prometheus.metrics.serviceMonitor.enabled: false + services.http.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Rendering serviceMonitor with default values - enabled manually. + set: + prometheus.metrics.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: monitoring.coreos.com/v1 + kind: ServiceMonitor + name: prometheus-fail2ban-exporter-unittest-http + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - equal: + path: spec.endpoints[0].enableHttp2 + value: true + - equal: + path: spec.endpoints[0].followRedirects + value: false + - equal: + path: spec.endpoints[0].honorLabels + value: false + - equal: + path: spec.endpoints[0].interval + value: 60s + - equal: + path: spec.endpoints[0].path + value: /metrics + - notExists: + path: spec.endpoints[0].relabelings + - equal: + path: spec.endpoints[0].scrapeTimeout + value: 30s + - equal: + path: spec.endpoints[0].scheme + value: http + - equal: + path: spec.endpoints[0].targetPort + value: 9191 + - contains: + path: spec.namespaceSelector.matchNames + content: + testing + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/service-name: http + +- it: Render serviceMonitor with custom annotations and labels. + set: + prometheus.metrics.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + prometheus.metrics.serviceMonitor.annotations: + foo: bar + prometheus.metrics.serviceMonitor.labels: + bar: foo + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/version: 0.1.0 + bar: foo + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + +- it: Change defaults + set: + prometheus.metrics.enabled: true + prometheus.metrics.serviceMonitor.enabled: true + prometheus.metrics.serviceMonitor.enableHttp2: false + prometheus.metrics.serviceMonitor.followRedirects: true + prometheus.metrics.serviceMonitor.honorLabels: true + prometheus.metrics.serviceMonitor.interval: "180s" + prometheus.metrics.serviceMonitor.path: "/my-metrics" + prometheus.metrics.serviceMonitor.relabelings: + - sourceLabels: [ container ] + separator: ";" + regex: "app" + replacement: "$1" + action: "drop" + prometheus.metrics.serviceMonitor.scrapeTimeout: "5s" + prometheus.metrics.serviceMonitor.scheme: "http" + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.endpoints[0].enableHttp2 + value: false + - equal: + path: spec.endpoints[0].followRedirects + value: true + - equal: + path: spec.endpoints[0].honorLabels + value: true + - equal: + path: spec.endpoints[0].interval + value: 180s + - equal: + path: spec.endpoints[0].path + value: /my-metrics + - contains: + path: spec.endpoints[0].relabelings + content: + sourceLabels: [ container ] + separator: ";" + regex: "app" + replacement: "$1" + action: "drop" + - equal: + path: spec.endpoints[0].scrapeTimeout + value: 5s + - equal: + path: spec.endpoints[0].scheme + value: http \ No newline at end of file diff --git a/unittests/services/http.yaml b/unittests/services/http.yaml new file mode 100644 index 0000000..c0995d0 --- /dev/null +++ b/unittests/services/http.yaml @@ -0,0 +1,174 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Service http template (basic) +release: + name: prometheus-fail2ban-exporter-unittest + namespace: testing +templates: +- templates/prometheus-fail2ban-exporter/serviceHTTP.yaml +tests: +- it: Skip service when disabled. + set: + services.http.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Rendering service with default values. + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: Service + name: prometheus-fail2ban-exporter-unittest-http + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/service-name: http + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + - notExists: + path: spec.externalIPs + - notExists: + path: spec.externalTrafficPolicy + - equal: + path: spec.internalTrafficPolicy + value: Cluster + - notExists: + path: spec.ipFamilies + - notExists: + path: spec.loadBalancerClass + - notExists: + path: spec.loadBalancerIP + - notExists: + path: spec.loadBalancerSourceRanges + - equal: + path: spec.ports[0].name + value: http + - equal: + path: spec.ports[0].protocol + value: TCP + - equal: + path: spec.ports[0].port + value: 9191 + - equal: + path: spec.selector + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/name: prometheus-fail2ban-exporter + - equal: + path: spec.sessionAffinity + value: None + - notExists: + path: spec.sessionAffinityConfig + - equal: + path: spec.type + value: ClusterIP + +- it: Require internalTrafficPolicy. + set: + services.http.internalTrafficPolicy: "" + asserts: + - failedTemplate: + errorMessage: No internal traffic policy defined! + +- it: Require port. + set: + services.http.port: "" + asserts: + - failedTemplate: + errorMessage: No service port defined! + +- it: Require sessionAffinity. + set: + services.http.sessionAffinity: "" + asserts: + - failedTemplate: + errorMessage: No session affinity defined! + +- it: Require service type. + set: + services.http.type: "" + asserts: + - failedTemplate: + errorMessage: No service type defined! + +- it: Render service with custom annotations and labels. + set: + services.http.annotations: + foo: bar + services.http.labels: + bar: foo + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: prometheus-fail2ban-exporter-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: prometheus-fail2ban-exporter + app.kubernetes.io/service-name: http + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: prometheus-fail2ban-exporter-0.1.0 + bar: foo + +- it: Change defaults + set: + services.http.externalIPs: + - "10.11.12.13/32" + services.http.externalTrafficPolicy: Local + services.http.internalTrafficPolicy: Local + services.http.ipFamilies: + - IPv4 + services.http.loadBalancerClass: aws + services.http.loadBalancerIP: "11.12.13.14" + services.http.loadBalancerSourceRanges: + - "11.12.0.0/17" + services.http.port: 10443 + services.http.sessionAffinity: ClientIP + services.http.type: LoadBalancer + asserts: + - equal: + path: spec.externalIPs + value: + - 10.11.12.13/32 + - equal: + path: spec.externalTrafficPolicy + value: Local + - equal: + path: spec.internalTrafficPolicy + value: Local + - equal: + path: spec.ipFamilies + value: + - IPv4 + - equal: + path: spec.loadBalancerClass + value: aws + - equal: + path: spec.loadBalancerIP + value: "11.12.13.14" + - equal: + path: spec.loadBalancerSourceRanges + value: + - "11.12.0.0/17" + - equal: + path: spec.ports[0].port + value: 10443 + - equal: + path: spec.sessionAffinity + value: ClientIP + - equal: + path: spec.type + value: LoadBalancer \ No newline at end of file diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..dabb439 --- /dev/null +++ b/values.yaml @@ -0,0 +1,395 @@ +# Declare variables to be passed into your templates. +## @section Global +## @param nameOverride Individual release name suffix. +## @param fullnameOverride Override the complete release name logic. +nameOverride: "" +fullnameOverride: "" + +## @section Configuration +config: + webConfig: + ## @param config.webConfig.existingSecret.enabled Mount an existing secret containing the key `webConfig.yaml`. + ## @param config.webConfig.existingSecret.secretName Name of the existing secret containing the key `webConfig.yaml`. + existingSecret: + enabled: false + secretName: "" + + ## @param config.webConfig.secret.annotations Additional annotations of the secret containing the `webConfig.yaml`. + ## @param config.webConfig.secret.labels Additional labels of the secret containing the `webConfig.yaml`. + ## @param config.webConfig.secret.webConfig Content of the `webConfig.yaml`. + ## @skip config.webConfig.secret.webConfig Skip individual web configuration. + secret: + annotations: {} + labels: {} + webConfig: {} + # basic_auth_users: + # prom: + # http_server_config: + # http2: true + # tls_server_config: + # cert_file: /path/to/cert.pem + # client_allowed_sans: + # - fail2ban.example.local + # client_ca_file: /path/to/ca.pem + # key_file: /path/to/key.pem + # max_version: TLS13 + # min_version: TLS12 + +## @section Daemonset +daemonSet: + ## @param daemonSet.annotations Additional deployment annotations. + ## @param daemonSet.labels Additional deployment labels. + annotations: {} + labels: {} + + ## @param daemonSet.additionalContainers List of additional containers. + additionalContainers: [] + # - command: [ "sh", "-c", "echo hello world" ] + # image: "docker.io/library/busybox:latest" + # name: side-car + + ## @param daemonSet.affinity Affinity for the fail2ban-exporter daemonSet. + affinity: {} + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 20 + # preference: + # matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + + ## @param daemonSet.initContainers List of additional init containers. + initContainers: [] + # - command: [ "sh", "-c", "echo hello world" ] + # image: "docker.io/library/busybox:latest" + # name: init + + ## @param daemonSet.dnsConfig dnsConfig of the fail2ban-exporter daemonSet. + dnsConfig: {} + # nameservers: + # - 192.0.2.1 # this is an example + # searches: + # - ns1.svc.cluster-domain.example + # - my.dns.search.suffix + # options: + # - name: ndots + # value: "2" + # - name: edns0 + + ## @param daemonSet.dnsPolicy dnsPolicy of the fail2ban-exporter daemonSet. + dnsPolicy: "" + + ## @param daemonSet.hostname Individual hostname of the pod. + ## @param daemonSet.subdomain Individual domain of the pod. + hostname: "" + subdomain: "" + + ## @param daemonSet.hostNetwork Use the kernel network namespace of the host system. + hostNetwork: false + + ## @param daemonSet.imagePullSecrets Secret to use for pulling the image. + imagePullSecrets: [] + # - name: "my-custom-secret" + + fail2banExporter: + ## @param daemonSet.fail2banExporter.args Arguments passed to the fail2ban-exporter container. + args: [] + + ## @param daemonSet.fail2banExporter.env List of environment variables for the fail2ban-exporter container. + env: [] + # - name: SPECIAL_ENV_A + # value: special-key + # - name: SPECIAL_ENV + # valueFrom: + # configMapKeyRef: + # name: special-config + # key: special-key + # - name: SPECIAL_ENV + # valueFrom: + # secretKeyRef: + # name: special-secret + # key: special-key + + ## @param daemonSet.fail2banExporter.envFrom List of environment variables mounted from configMaps or secrets for the fail2ban-exporter container. + envFrom: [] + # - configMapRef: + # name: special-config + # - secretRef: + # name: special-secret + + ## @param daemonSet.fail2banExporter.image.registry Image registry, eg. `docker.io`. + ## @param daemonSet.fail2banExporter.image.repository Image repository, eg. `library/busybox`. + ## @param daemonSet.fail2banExporter.image.tag Custom image tag, eg. `0.1.0`. Defaults to `appVersion`. + ## @param daemonSet.fail2banExporter.image.pullPolicy Image pull policy. + image: + registry: git.cryptic.systems + repository: volker.raschek/prometheus-fail2ban-exporter + tag: "" + pullPolicy: IfNotPresent + + ## @param daemonSet.fail2banExporter.resources CPU and memory resources of the pod. + resources: {} + # limits: + # cpu: + # ephemeral-storage: + # memory: + # requests: + # cpu: + # ephemeral-storage: + # memory: + + ## @param daemonSet.fail2banExporter.securityContext Security context of the container of the daemonSet. + securityContext: {} + # capabilities: + # add: + # - NET_RAW + # drop: + # - ALL + # privileged: false + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + + ## @param daemonSet.fail2banExporter.volumeMounts Additional volume mounts. + ## @skip daemonSet.fail2banExporter.volumeMounts Skip individual volume mounts configuration. + volumeMounts: + - name: socket + mountPath: /var/run/fail2ban + + ## @param daemonSet.nodeSelector NodeSelector of the fail2ban-exporter daemonSet. + nodeSelector: {} + + ## @param daemonSet.priorityClassName PriorityClassName of the fail2ban-exporter daemonSet. + priorityClassName: "" + + ## @param daemonSet.restartPolicy Restart policy of the fail2ban-exporter daemonSet. + restartPolicy: "" + + ## @param daemonSet.securityContext Security context of the fail2ban-exporter daemonSet. + securityContext: {} + # fsGroup: 2000 + + ## @param daemonSet.updateStrategy.rollingUpdate.maxSurge The maximum number of pods that can be scheduled above the desired number of pods during a rolling update. + ## @param daemonSet.updateStrategy.rollingUpdate.maxUnavailable The maximum number of pods that can be unavailable during a rolling update. + ## @param daemonSet.updateStrategy.type Strategy type - `OnDelete` or `RollingUpdate`. + updateStrategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + type: "RollingUpdate" + + ## @param daemonSet.terminationGracePeriodSeconds How long to wait until forcefully kill the pod. + terminationGracePeriodSeconds: 60 + + ## @param daemonSet.tolerations Tolerations of the fail2ban-exporter daemonSet. + tolerations: [] + # - key: database/type + # operator: Equal + # value: fail2ban + # effect: NoSchedule + + ## @param daemonSet.topologySpreadConstraints TopologySpreadConstraints of the fail2ban-exporter daemonSet. + topologySpreadConstraints: [] + # - topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: prometheus-fail2ban-exporter + + ## @param daemonSet.volumes Additional volumes to mount into the pods of the prometheus-exporter daemonset. + ## @skip daemonSet.volumes Skip individual volumes configuration. + volumes: + - name: socket + hostPath: + path: /var/run/fail2ban + type: Directory + # - name: my-configmap-volume + # config: + # name: my-configmap + # - name: my-secret-volume + # secret: + # secretName: my-secret + +## @section Grafana +## @param grafana.enabled Enable integration into Grafana. Require the Prometheus operator daemonSet. +grafana: + enabled: false + + ## @param grafana.dashboardDiscoveryLabels Labels that Grafana uses to discover resources. The labels may vary depending on the Grafana daemonSet. + ## @skip grafana.dashboardDiscoveryLabels + dashboardDiscoveryLabels: + grafana_dashboard: "1" + + dashboards: + ## @param grafana.dashboards.fail2banExporter.enabled Enable deployment of Grafana dashboard `fail2banExporter`. + ## @param grafana.dashboards.fail2banExporter.annotations Additional configmap annotations. + ## @param grafana.dashboards.fail2banExporter.labels Additional configmap labels. + fail2banExporter: + enabled: true + annotations: {} + labels: {} + +## @section Ingress +ingress: + ## @param ingress.enabled Enable creation of an ingress resource. Requires, that the http service is also enabled. + ## @param ingress.className Ingress class. + ## @param ingress.annotations Additional ingress annotations. + ## @param ingress.labels Additional ingress labels. + enabled: false + className: "nginx" + annotations: {} + labels: {} + + ## @param ingress.hosts Ingress specific configuration. Specification only required when another ingress controller is used instead of `t1k. + ## @skip ingress.hosts Skip individual host configuration. + hosts: [] + # - host: fail2ban-exporter.example.local + # paths: + # - path: / + # pathType: Prefix + + ## @param ingress.tls Ingress TLS settings. Specification only required when another ingress controller is used instead of `t1k``. + ## @skip ingress.tls Skip individual TLS configuration. + tls: [] + # - secretName: fail2ban-exporter-http-tls + # hosts: + # - fail2ban-exporter.example.local + +## @section Pod disruption +## @param podDisruptionBudget Pod disruption budget. +podDisruptionBudget: {} +# maxUnavailable: 1 +# minAvailable: 1 + +## @section Network +## @param networkPolicies Deploy network policies based on the used container network interface (CNI) implementation - like calico or weave. +networkPolicies: {} + +## @section Prometheus +prometheus: + ## @param prometheus.metrics.enabled Enable of scraping metrics by Prometheus. + metrics: + enabled: true + + ## @param prometheus.metrics.podMonitor.enabled Enable creation of a podMonitor. Excludes the existence of a serviceMonitor resource. + ## @param prometheus.metrics.podMonitor.annotations Additional podMonitor annotations. + ## @param prometheus.metrics.podMonitor.enableHttp2 Enable HTTP2. + ## @param prometheus.metrics.podMonitor.followRedirects FollowRedirects configures whether scrape requests follow HTTP 3xx redirects. + ## @param prometheus.metrics.podMonitor.honorLabels Honor labels. + ## @param prometheus.metrics.podMonitor.labels Additional podMonitor labels. + ## @param prometheus.metrics.podMonitor.interval Interval at which metrics should be scraped. If not specified Prometheus' global scrape interval is used. + ## @param prometheus.metrics.podMonitor.path HTTP path for scraping Prometheus metrics. + ## @param prometheus.metrics.podMonitor.relabelings RelabelConfigs to apply to samples before scraping. Prometheus Operator automatically adds relabelings for a few standard Kubernetes fields. + ## @param prometheus.metrics.podMonitor.scrapeTimeout Timeout after which the scrape is ended. If not specified, global Prometheus scrape timeout is used. + ## @param prometheus.metrics.podMonitor.scheme HTTP scheme to use for scraping. For example `http` or `https`. + ## @param prometheus.metrics.podMonitor.tlsConfig TLS configuration to use when scraping the metric endpoint by Prometheus. + ## @skip prometheus.metrics.podMonitor.tlsConfig Skip individual TLS configuration. + podMonitor: + enabled: false + annotations: {} + enableHttp2: true + followRedirects: false + honorLabels: false + labels: {} + interval: "60s" + path: "/metrics" + relabelings: [] + scrapeTimeout: "30s" + scheme: "http" + tlsConfig: {} + + ## @param prometheus.metrics.serviceMonitor.enabled Enable creation of a serviceMonitor. Excludes the existence of a podMonitor resource. + ## @param prometheus.metrics.serviceMonitor.annotations Additional serviceMonitor annotations. + ## @param prometheus.metrics.serviceMonitor.labels Additional serviceMonitor labels. + ## @param prometheus.metrics.serviceMonitor.enableHttp2 Enable HTTP2. + ## @param prometheus.metrics.serviceMonitor.followRedirects FollowRedirects configures whether scrape requests follow HTTP 3xx redirects. + ## @param prometheus.metrics.serviceMonitor.honorLabels Honor labels. + ## @param prometheus.metrics.serviceMonitor.interval Interval at which metrics should be scraped. If not specified Prometheus' global scrape interval is used. + ## @param prometheus.metrics.serviceMonitor.path HTTP path for scraping Prometheus metrics. + ## @param prometheus.metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping. Prometheus Operator automatically adds relabelings for a few standard Kubernetes fields. + ## @param prometheus.metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended. If not specified, global Prometheus scrape timeout is used. + ## @param prometheus.metrics.serviceMonitor.scheme HTTP scheme to use for scraping. For example `http` or `https`. + ## @param prometheus.metrics.serviceMonitor.tlsConfig TLS configuration to use when scraping the metric endpoint by Prometheus. + ## @skip prometheus.metrics.serviceMonitor.tlsConfig Skip individual TLS configuration. + serviceMonitor: + enabled: false + annotations: {} + labels: {} + enableHttp2: true + followRedirects: false + honorLabels: false + interval: "60s" + path: "/metrics" + relabelings: [] + scrapeTimeout: "30s" + scheme: "http" + tlsConfig: {} + + ## @param prometheus.rules Array of Prometheus rules for monitoring the application and triggering alerts. + ## @skip prometheus.rules Skip individual Prometheus rules. + rules: [] + +## @section Service +## @param services.http.enabled Enable the service. +## @param services.http.annotations Additional service annotations. +## @param services.http.externalIPs External IPs for the service. +## @param services.http.externalTrafficPolicy If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster external traffic. Furthermore, this enables source IP preservation. +## @param services.http.internalTrafficPolicy If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster internal traffic. +## @param services.http.ipFamilies IPFamilies is list of IP families (e.g. `IPv4`, `IPv6`) assigned to this service. This field is usually assigned automatically based on cluster configuration and only required for customization. +## @param services.http.labels Additional service labels. +## @param services.http.loadBalancerClass LoadBalancerClass is the class of the load balancer implementation this Service belongs to. Requires service from type `LoadBalancer`. +## @param services.http.loadBalancerIP LoadBalancer will get created with the IP specified in this field. Requires service from type `LoadBalancer`. +## @param services.http.loadBalancerSourceRanges Source range filter for LoadBalancer. Requires service from type `LoadBalancer`. +## @param services.http.port Port to forward the traffic to. +## @param services.http.sessionAffinity Supports `ClientIP` and `None`. Enable client IP based session affinity via `ClientIP`. +## @param services.http.sessionAffinityConfig Contains the configuration of the session affinity. +## @param services.http.type Kubernetes service type for the traffic. +services: + http: + enabled: true + annotations: {} + externalIPs: [] + externalTrafficPolicy: "Cluster" + internalTrafficPolicy: "Cluster" + ipFamilies: [] + labels: {} + loadBalancerClass: "" + loadBalancerIP: "" + loadBalancerSourceRanges: [] + port: 9191 + sessionAffinity: "None" + sessionAffinityConfig: {} + type: "ClusterIP" + +## @section ServiceAccount +serviceAccount: + ## @param serviceAccount.existing.enabled Use an existing service account instead of creating a new one. Assumes that the user has all the necessary kubernetes API authorizations. + ## @param serviceAccount.existing.serviceAccountName Name of the existing service account. + existing: + enabled: false + serviceAccountName: "" + + ## @param serviceAccount.new.annotations Additional service account annotations. + ## @param serviceAccount.new.labels Additional service account labels. + ## @param serviceAccount.new.automountServiceAccountToken Enable/disable auto mounting of the service account token. + ## @param serviceAccount.new.imagePullSecrets ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this serviceAccount. + ## @param serviceAccount.new.secrets Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. + new: + annotations: {} + labels: {} + automountServiceAccountToken: true + imagePullSecrets: [] + # - name: "my-image-pull-secret" + secrets: [] + # - name: "my-secret" + # namespace: "my-namespace" + # fieldPath: "my-field"