commit d3efe6d34cde96363c10c7b876448e75bdc31be9 Author: 0xFEEDC0DE64 Date: Mon Oct 19 08:43:32 2026 +0200 Add Argo CD Helm chart release diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml new file mode 100644 index 0000000..5ba1fb4 --- /dev/null +++ b/.gitea/workflows/publish.yml @@ -0,0 +1,105 @@ +name: Validate and publish mktxp chart + +on: + push: + pull_request: + +jobs: + validate: + runs-on: linux_amd64 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Helm + run: | + curl --fail --silent --show-error --location --output "${RUNNER_TEMP}/helm.tar.gz" https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz + tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm + mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm" + chmod 0700 "${RUNNER_TEMP}/helm" + echo "${RUNNER_TEMP}" >> "$GITHUB_PATH" + - run: ./test.sh + + publish: + if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' + needs: validate + runs-on: linux_amd64 + outputs: + chart_version: ${{ steps.chart.outputs.version }} + source_sha: ${{ gitea.sha }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Helm + run: | + curl --fail --silent --show-error --location --output "${RUNNER_TEMP}/helm.tar.gz" https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz + tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm + mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm" + chmod 0700 "${RUNNER_TEMP}/helm" + echo "${RUNNER_TEMP}" >> "$GITHUB_PATH" + - id: chart + name: Derive chart version + env: + COMMIT_SHA: ${{ gitea.sha }} + run: | + set -euo pipefail + git fetch --tags origin main + test "$(git rev-parse origin/main)" = "$COMMIT_SHA" + description=$(git describe --tags --long --match 'v[0-9]*' --dirty) + case "$description" in *-dirty) echo 'Refusing dirty chart tree' >&2; exit 1 ;; esac + version=${description#v} + case "$version" in *-0-g*) version=${version%%-0-g*} ;; esac + echo "version=${version}" >> "$GITHUB_OUTPUT" + - name: Package and publish + env: + PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }} + PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} + CHART_VERSION: ${{ steps.chart.outputs.version }} + run: | + set -euo pipefail + test -n "$PACKAGE_USERNAME" && test -n "$PACKAGE_TOKEN" + helm lint chart + ./test.sh + helm package chart --version "$CHART_VERSION" --destination "$RUNNER_TEMP" + curl --fail --silent --show-error --user "${PACKAGE_USERNAME}:${PACKAGE_TOKEN}" \ + --request POST --upload-file "${RUNNER_TEMP}/mktxp-${CHART_VERSION}.tgz" \ + https://code.brunner.ninja/api/packages/feedc0de/helm/api/charts + + promote: + if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' + needs: publish + runs-on: linux_amd64 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Promote GitOps chart version + env: + GITOPS_TOKEN: ${{ secrets.GITOPS_TOKEN }} + CHART_VERSION: ${{ needs.publish.outputs.chart_version }} + COMMIT_SHA: ${{ gitea.sha }} + run: | + set -euo pipefail + test -n "$GITOPS_TOKEN" + git fetch origin main + test "$(git rev-parse origin/main)" = "$COMMIT_SHA" + auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n') + git -c "http.extraheader=AUTHORIZATION: basic ${auth}" clone --depth=1 \ + https://code.brunner.ninja/feedc0de/homelab-gitops.git "$RUNNER_TEMP/gitops" + unset auth + cd "$RUNNER_TEMP/gitops" + gitops_auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n') + unset GITOPS_TOKEN + git config user.name feedc0de + git config user.email feedc0de@users.noreply.code.brunner.ninja + sed -i -E "0,/targetRevision: [^ ]+/s//targetRevision: ${CHART_VERSION}/" bootstrap/mktxp.yaml + if git diff --quiet; then exit 0; fi + git add bootstrap/mktxp.yaml + git commit -m "Deploy mktxp chart ${CHART_VERSION}" + for attempt in 1 2 3; do + if git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" push origin HEAD:main; then exit 0; fi + git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" fetch origin main + git rebase origin/main + done + exit 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..466f78c --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# mktxp Helm chart + +Maintained adapter chart for the public `ghcr.io/akpw/mktxp` image. It deploys +one exporter, exposes its metrics Service, and can optionally create a +Prometheus Operator `ServiceMonitor`. + +The chart references an existing Secret containing the mktxp configuration; it +never stores MikroTik credentials. For this homelab, Argo CD consumes the chart +and [cluster values](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/mktxp). + +Install on another cluster after creating the `mktxp-credentials` Secret: + +```sh +helm repo add brunner.ninja https://brunner.ninja/charts +helm repo update brunner.ninja +helm upgrade --install mktxp brunner.ninja/mktxp \ + --namespace default --create-namespace --wait --timeout 5m +``` + +Chart tags version this adapter; `appVersion` selects the upstream image. diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..796c8b6 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mktxp +description: Helm chart for the MikroTik mktxp Prometheus exporter +type: application +version: 0.1.0 +appVersion: "2.1.0" diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..d2c5316 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mktxp-exporter + labels: + app: mktxp-exporter + app.kubernetes.io/name: mktxp + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: mktxp-exporter + template: + metadata: + labels: + app: mktxp-exporter + app.kubernetes.io/name: mktxp + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + automountServiceAccountToken: false + containers: + - name: mktxp + image: {{ printf "%s:%s" .Values.image.repository .Chart.AppVersion | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["mktxp"] + args: ["--cfg-dir", "/mktxp_config", "export"] + ports: + - name: metrics + containerPort: {{ .Values.service.port }} + protocol: TCP + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: mktxp-credentials + mountPath: /mktxp_config + readOnly: true + volumes: + - name: mktxp-credentials + secret: + secretName: {{ .Values.existingConfigSecret | quote }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 0000000..b937d8c --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: mktxp-exporter + labels: + app: mktxp-exporter + app.kubernetes.io/name: mktxp + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + selector: + app: mktxp-exporter + ports: + - name: metrics + port: {{ .Values.service.port }} + targetPort: metrics + protocol: TCP diff --git a/chart/templates/servicemonitor.yaml b/chart/templates/servicemonitor.yaml new file mode 100644 index 0000000..832e4d7 --- /dev/null +++ b/chart/templates/servicemonitor.yaml @@ -0,0 +1,31 @@ +{{- if .Values.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: mktxp-exporter + labels: + app.kubernetes.io/name: mktxp + app.kubernetes.io/instance: {{ .Release.Name }} + {{- with .Values.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app: mktxp-exporter + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + endpoints: + - port: metrics + path: /metrics + interval: {{ .Values.serviceMonitor.interval }} + scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} + honorLabels: true + relabelings: + - regex: 'pod|container|instance|endpoint|namespace|service' + action: labeldrop + metricRelabelings: + - regex: 'instance|pod|container' + action: labeldrop +{{- end }} diff --git a/chart/values.schema.json b/chart/values.schema.json new file mode 100644 index 0000000..5557d1d --- /dev/null +++ b/chart/values.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["image", "existingConfigSecret", "serviceMonitor"], + "properties": { + "replicaCount": {"type": "integer", "minimum": 1}, + "image": { + "type": "object", + "required": ["repository", "pullPolicy"], + "properties": { + "repository": {"type": "string", "minLength": 1}, + "pullPolicy": {"type": "string", "enum": ["Always", "IfNotPresent", "Never"]} + }, + "additionalProperties": false + }, + "existingConfigSecret": {"type": "string", "minLength": 1}, + "service": {"type": "object"}, + "serviceMonitor": { + "type": "object", + "required": ["enabled", "labels"], + "properties": { + "enabled": {"type": "boolean"}, + "labels": {"type": "object"}, + "interval": {"type": "string"}, + "scrapeTimeout": {"type": "string"} + }, + "additionalProperties": true + }, + "resources": {"type": "object"} + }, + "additionalProperties": true +} diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..4e031c5 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,21 @@ +replicaCount: 1 + +image: + repository: ghcr.io/akpw/mktxp + pullPolicy: IfNotPresent + +existingConfigSecret: mktxp-credentials + +service: + port: 49090 + +serviceMonitor: + enabled: false + labels: {} + interval: 30s + scrapeTimeout: 20s + +resources: + limits: + cpu: 500m + memory: 512Mi diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..db932f7 --- /dev/null +++ b/install.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -eu + +release=${RELEASE_NAME:-mktxp} +namespace=${NAMESPACE:-default} + +helm repo add brunner.ninja https://brunner.ninja/charts --force-update +helm repo update brunner.ninja +if [ -n "${VALUES_FILE:-}" ]; then + helm upgrade --install "$release" brunner.ninja/mktxp \ + --namespace "$namespace" --create-namespace \ + --values "$VALUES_FILE" --wait --timeout 5m +else + helm upgrade --install "$release" brunner.ninja/mktxp \ + --namespace "$namespace" --create-namespace --wait --timeout 5m +fi diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..133248a --- /dev/null +++ b/test.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +chart_dir=$(CDPATH= cd -- "$(dirname -- "$0")/chart" && pwd) +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT + +helm lint "$chart_dir" +helm template mktxp "$chart_dir" --namespace default >"$workdir/default.yaml" +helm template mktxp "$chart_dir" --namespace default \ + --set serviceMonitor.enabled=true \ + --set serviceMonitor.labels.release=prometheus >"$workdir/monitor.yaml" + +grep -q 'name: mktxp-exporter' "$workdir/default.yaml" +grep -q 'secretName: "mktxp-credentials"' "$workdir/default.yaml" +grep -q 'kind: ServiceMonitor' "$workdir/monitor.yaml" +app_version=$(sed -n 's/^appVersion: *"\{0,1\}\([^" ]*\).*/\1/p' "$chart_dir/Chart.yaml") +image_repository=$(sed -n 's/^ repository: *//p' "$chart_dir/values.yaml") +grep -Fq "${image_repository}:${app_version}" "$workdir/default.yaml"