From befcc24d8a99ec311190b6afdef48b118c036567 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Thu, 25 Jun 2026 23:51:15 +0200 Subject: [PATCH 01/12] MCP Helm chart: Add support for Traefik ingress --- DEPLOYMENT_GUIDE.md | 53 ++++++++++++++++++- src/helm-charts/rosetta-mcp-server/Chart.yaml | 2 +- .../rosetta-mcp-server/templates/ingress.yaml | 16 +++++- .../templates/traefik-middlewares.yaml | 25 +++++++++ .../values-dev.example.yaml | 8 +++ .../values-prod.example.yaml | 8 +++ .../rosetta-mcp-server/values.yaml | 11 ++++ 7 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index 22f39d0a..c030d569 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -248,7 +248,52 @@ helm install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ 1. **Image** — `image.repository` defaults to `griddynamics/rosetta-mcp`; set `image.tag` or rely on the chart defaulting the tag to [`appVersion`](src/helm-charts/rosetta-mcp-server/Chart.yaml) in the Deployment template. 2. **Rosetta backend** — Set `env.vars` so `ROSETTA_SERVER_URL` resolves to Rosetta Server (in-cluster DNS or ingress URL). 3. **API key** — Supply `ROSETTA_API_KEY` via `env.secrets` (`secretKeyRef`). Create the Kubernetes Secret first or use `eso` to sync it. -4. **Ingress** — Set `ingress.host` and annotations. Defaults use an NGINX-style controller and placeholder host `rosetta-mcp.local`. +4. **Ingress** — Set `ingress.host`. The chart supports both **nginx** (default) and **Traefik** ingress controllers. + + **nginx** (default): + + ```yaml + ingress: + className: nginx + host: rosetta.example.com + annotations: + kubernetes.io/ingress.class: nginx + ``` + + **Traefik** — set `className: traefik` and configure middlewares under `ingress.traefik`: + + ```yaml + ingress: + className: traefik + host: rosetta.example.com + annotations: {} + traefik: + rateLimit: + average: 20 + burst: 200 + period: "1s" + ``` + + When `rateLimit.average` is set, the chart creates a Traefik `Middleware` CRD + (`-rate-limit`) in the release namespace and wires it into the Ingress + annotation automatically. + + To reference additional external middlewares (e.g. a platform-wide chain managed + outside this chart), list them in `ingress.traefik.middlewares`: + + ```yaml + ingress: + traefik: + middlewares: + - "traefik-my-chain@kubernetescrd" + rateLimit: + average: 20 + burst: 200 + period: "1s" + ``` + + External middlewares are rendered first in the annotation, followed by the + per-release rate limit. 5. **TLS (production)** — Enable encrypted client traffic before production use. Uncomment and complete the [`ingress.tls`](src/helm-charts/rosetta-mcp-server/values.yaml) block in your overlay so Ingress terminates HTTPS with a TLS `Secret` (or terminate TLS upstream and align hostnames). HTTP-only defaults are unsuitable for production; OAuth and user trust depend on HTTPS. Full environment-variable semantics for OAuth, Redis, analytics, and modes are the same as the application runtime; see [rosetta-mcp-server — Configuration](src/rosetta-mcp-server/README.md#configuration). @@ -273,6 +318,7 @@ helm upgrade --install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ | [`templates/deployment.yaml`](src/helm-charts/rosetta-mcp-server/templates/deployment.yaml) | Deployment, env, resources | | [`templates/service.yaml`](src/helm-charts/rosetta-mcp-server/templates/service.yaml) | ClusterIP and session affinity | | [`templates/ingress.yaml`](src/helm-charts/rosetta-mcp-server/templates/ingress.yaml) | Optional Ingress | +| [`templates/traefik-middlewares.yaml`](src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml) | Traefik Middleware CRDs (when `className: traefik`) | | [`templates/hpa.yaml`](src/helm-charts/rosetta-mcp-server/templates/hpa.yaml) | Optional HPA | | [`templates/external-secret.yaml`](src/helm-charts/rosetta-mcp-server/templates/external-secret.yaml) | Optional ExternalSecret (`eso.*`) | | [`templates/serviceaccount.yaml`](src/helm-charts/rosetta-mcp-server/templates/serviceaccount.yaml) | ServiceAccount | @@ -317,6 +363,11 @@ Base keys in [`src/helm-charts/rosetta-mcp-server/values.yaml`](src/helm-charts/ | `replicaCount` | `1` | Static replicas when HPA disabled | | `autoscaling.enabled` | `false` | HPA toggle | | `ingress.enabled` | `true` | Ingress resource | +| `ingress.className` | `nginx` | Ingress controller (`nginx` or `traefik`) | +| `ingress.traefik.middlewares` | `[]` | External Traefik middleware references | +| `ingress.traefik.rateLimit.average` | (unset) | Rate limit — requests per period | +| `ingress.traefik.rateLimit.burst` | (unset) | Rate limit — max burst size | +| `ingress.traefik.rateLimit.period` | (unset) | Rate limit — period (e.g. `"1s"`) | | `ingress.tls` | Commented in base [`values.yaml`](src/helm-charts/rosetta-mcp-server/values.yaml); enable for production | HTTPS termination at Ingress | | `service.sessionAffinity` | `ClientIP` | Pod stickiness | | `eso.enabled` | `false` | External Secrets Operator sync | diff --git a/src/helm-charts/rosetta-mcp-server/Chart.yaml b/src/helm-charts/rosetta-mcp-server/Chart.yaml index 7fdde5e3..d13cf0aa 100644 --- a/src/helm-charts/rosetta-mcp-server/Chart.yaml +++ b/src/helm-charts/rosetta-mcp-server/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: rosetta-mcp-helm-chart description: Helm chart for Rosetta MCP server - the consulting layer between IDEs and Rosetta knowledge base (RAGFlow) type: application -version: 0.3.0 +version: 0.4.0 appVersion: "2.0.0" keywords: - rosetta diff --git a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml index 2f897e27..315572df 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml @@ -6,10 +6,22 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "rosetta-mcp.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + {{- if eq (.Values.ingress.className | default "") "traefik" }} + {{- $middlewares := list }} + {{- range (.Values.ingress.traefik.middlewares | default list) }} + {{- $middlewares = append $middlewares . }} + {{- end }} + {{- if and .Values.ingress.traefik .Values.ingress.traefik.rateLimit .Values.ingress.traefik.rateLimit.average }} + {{- $middlewares = append $middlewares (printf "%s-%s-rate-limit@kubernetescrd" $.Release.Namespace (include "rosetta-mcp.fullname" $)) }} + {{- end }} + {{- if $middlewares }} + traefik.ingress.kubernetes.io/router.middlewares: {{ join "," $middlewares }} + {{- end }} + {{- end }} + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} - {{- end }} + {{- end }} spec: {{- if .Values.ingress.className }} ingressClassName: {{ .Values.ingress.className | quote }} diff --git a/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml new file mode 100644 index 00000000..96a3426f --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.ingress.enabled (eq (.Values.ingress.className | default "") "traefik") }} + +{{- if and .Values.ingress.traefik .Values.ingress.traefik.rateLimit .Values.ingress.traefik.rateLimit.average }} +--- +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: {{ include "rosetta-mcp.fullname" . }}-rate-limit + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className }} + labels: + {{- include "rosetta-mcp.labels" . | nindent 4 }} +spec: + rateLimit: + average: {{ .Values.ingress.traefik.rateLimit.average }} + {{- with .Values.ingress.traefik.rateLimit.burst }} + burst: {{ . }} + {{- end }} + {{- with .Values.ingress.traefik.rateLimit.period }} + period: {{ . | quote }} + {{- end }} +{{- end }} + +{{- end }} diff --git a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml index 13ecc8f1..f26e7502 100644 --- a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml @@ -7,6 +7,14 @@ image: ingress: host: rosetta-dev.example.com + # Traefik example (uncomment to switch from nginx): + # className: traefik + # annotations: {} + # traefik: + # rateLimit: + # average: 20 + # burst: 200 + # period: "1s" env: enabled: true diff --git a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml index d923a9b2..fa3a2bce 100644 --- a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml @@ -6,6 +6,14 @@ image: ingress: host: rosetta.example.com + # Traefik example (uncomment to switch from nginx): + # className: traefik + # annotations: {} + # traefik: + # rateLimit: + # average: 20 + # burst: 200 + # period: "1s" env: enabled: true diff --git a/src/helm-charts/rosetta-mcp-server/values.yaml b/src/helm-charts/rosetta-mcp-server/values.yaml index e5e67b5c..0534210b 100644 --- a/src/helm-charts/rosetta-mcp-server/values.yaml +++ b/src/helm-charts/rosetta-mcp-server/values.yaml @@ -99,6 +99,17 @@ ingress: kubernetes.io/ingress.class: nginx paths: - / + # Traefik-specific settings (only used when className is "traefik"). + # When using Traefik, remove the nginx annotations above and set className to "traefik". + traefik: + # External middleware references (fully-qualified, e.g. "namespace-name@kubernetescrd"). + # Rendered first in the middleware chain annotation. + middlewares: [] + # Per-release rate limiting — creates a Traefik Middleware CRD in the release namespace. + # rateLimit: + # average: 100 + # burst: 200 + # period: "1s" # Production: enable TLS below (or terminate TLS upstream). Defaults leave MCP on HTTP at the Ingress until configured. # tls: # - secretName: rosetta-mcp-tls From 7d73e46a930fa93a159b8953d73a8f9dd8b1cf0c Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko <112936003+kkhristenko51@users.noreply.github.com> Date: Thu, 25 Jun 2026 23:57:54 +0200 Subject: [PATCH 02/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- DEPLOYMENT_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index c030d569..fd9468b6 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -275,7 +275,7 @@ helm install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ ``` When `rateLimit.average` is set, the chart creates a Traefik `Middleware` CRD - (`-rate-limit`) in the release namespace and wires it into the Ingress + (`-rate-limit`) in the release namespace and wires it into the Ingress annotation automatically. To reference additional external middlewares (e.g. a platform-wide chain managed From d6364efa0e6014aed75cc6756dc5b257abc52d9c Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Fri, 26 Jun 2026 00:03:30 +0200 Subject: [PATCH 03/12] Address review findings --- .../rosetta-mcp-server/templates/ingress.yaml | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml index 315572df..bdfa7d5a 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml @@ -6,22 +6,32 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "rosetta-mcp.labels" . | nindent 4 }} + {{- $traefikMw := "" }} + {{- if eq (.Values.ingress.className | default "") "traefik" }} + {{- $middlewares := list }} + {{- range (.Values.ingress.traefik.middlewares | default list) }} + {{- $middlewares = append $middlewares . }} + {{- end }} + {{- if and .Values.ingress.traefik .Values.ingress.traefik.rateLimit .Values.ingress.traefik.rateLimit.average }} + {{- $middlewares = append $middlewares (printf "%s-%s-rate-limit@kubernetescrd" $.Release.Namespace (include "rosetta-mcp.fullname" $)) }} + {{- end }} + {{- if $middlewares }} + {{- $traefikMw = join "," $middlewares }} + {{- end }} + {{- end }} + {{- $userAnnotations := .Values.ingress.annotations | default dict }} + {{- if $traefikMw }} + {{- $userAnnotations = omit $userAnnotations "traefik.ingress.kubernetes.io/router.middlewares" }} + {{- end }} + {{- if or $traefikMw $userAnnotations }} annotations: - {{- if eq (.Values.ingress.className | default "") "traefik" }} - {{- $middlewares := list }} - {{- range (.Values.ingress.traefik.middlewares | default list) }} - {{- $middlewares = append $middlewares . }} + {{- if $traefikMw }} + traefik.ingress.kubernetes.io/router.middlewares: {{ $traefikMw }} {{- end }} - {{- if and .Values.ingress.traefik .Values.ingress.traefik.rateLimit .Values.ingress.traefik.rateLimit.average }} - {{- $middlewares = append $middlewares (printf "%s-%s-rate-limit@kubernetescrd" $.Release.Namespace (include "rosetta-mcp.fullname" $)) }} - {{- end }} - {{- if $middlewares }} - traefik.ingress.kubernetes.io/router.middlewares: {{ join "," $middlewares }} - {{- end }} - {{- end }} - {{- with .Values.ingress.annotations }} + {{- with $userAnnotations }} {{- toYaml . | nindent 4 }} {{- end }} + {{- end }} spec: {{- if .Values.ingress.className }} ingressClassName: {{ .Values.ingress.className | quote }} From e5f07c3d7392829f1e7f10eaf2e27eafcd6ccabf Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko <112936003+kkhristenko51@users.noreply.github.com> Date: Fri, 26 Jun 2026 00:12:31 +0200 Subject: [PATCH 04/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/helm-charts/rosetta-mcp-server/templates/ingress.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml index bdfa7d5a..cb44ee1b 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml @@ -8,12 +8,13 @@ metadata: {{- include "rosetta-mcp.labels" . | nindent 4 }} {{- $traefikMw := "" }} {{- if eq (.Values.ingress.className | default "") "traefik" }} + {{- $traefik := .Values.ingress.traefik | default dict }} {{- $middlewares := list }} - {{- range (.Values.ingress.traefik.middlewares | default list) }} + {{- range ($traefik.middlewares | default list) }} {{- $middlewares = append $middlewares . }} {{- end }} - {{- if and .Values.ingress.traefik .Values.ingress.traefik.rateLimit .Values.ingress.traefik.rateLimit.average }} - {{- $middlewares = append $middlewares (printf "%s-%s-rate-limit@kubernetescrd" $.Release.Namespace (include "rosetta-mcp.fullname" $)) }} + {{- if and $traefik.rateLimit $traefik.rateLimit.average }} + {{- $middlewares = append $middlewares (printf "%s-rate-limit@kubernetescrd" (include "rosetta-mcp.fullname" $)) }} {{- end }} {{- if $middlewares }} {{- $traefikMw = join "," $middlewares }} From 40c17818fa391369f39ff4c00cb4981ff5593d5a Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Fri, 26 Jun 2026 08:32:41 +0200 Subject: [PATCH 05/12] Normalize middleware names --- src/helm-charts/rosetta-mcp-server/templates/ingress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml index cb44ee1b..6ac13b9d 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/ingress.yaml @@ -14,7 +14,7 @@ metadata: {{- $middlewares = append $middlewares . }} {{- end }} {{- if and $traefik.rateLimit $traefik.rateLimit.average }} - {{- $middlewares = append $middlewares (printf "%s-rate-limit@kubernetescrd" (include "rosetta-mcp.fullname" $)) }} + {{- $middlewares = append $middlewares (printf "%s@kubernetescrd" (regexReplaceAll "-{2,}" (printf "%s-rate-limit" (include "rosetta-mcp.fullname" $)) "-")) }} {{- end }} {{- if $middlewares }} {{- $traefikMw = join "," $middlewares }} From e8b5898642995806b7ec990992483b3baf1557d3 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Tue, 30 Jun 2026 19:01:23 +0200 Subject: [PATCH 06/12] Update values/examples, add unit tests and CI step to run them --- .github/workflows/publish-mcp-helm-chart.yml | 56 +++++++----- .../templates/traefik-middlewares.yaml | 2 - .../tests/deployment_test.yaml | 85 +++++++++++++++++++ .../tests/ingress_test.yaml | 76 +++++++++++++++++ .../rosetta-mcp-server/tests/pdb_test.yaml | 27 ++++++ .../tests/service_test.yaml | 26 ++++++ .../tests/traefik_middlewares_test.yaml | 81 ++++++++++++++++++ .../values-dev.example.yaml | 18 ++-- .../values-prod.example.yaml | 18 ++-- .../rosetta-mcp-server/values.yaml | 74 ++++++++-------- 10 files changed, 388 insertions(+), 75 deletions(-) create mode 100644 src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml create mode 100644 src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml create mode 100644 src/helm-charts/rosetta-mcp-server/tests/pdb_test.yaml create mode 100644 src/helm-charts/rosetta-mcp-server/tests/service_test.yaml create mode 100644 src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml diff --git a/.github/workflows/publish-mcp-helm-chart.yml b/.github/workflows/publish-mcp-helm-chart.yml index cf465ee8..e03d1ec4 100644 --- a/.github/workflows/publish-mcp-helm-chart.yml +++ b/.github/workflows/publish-mcp-helm-chart.yml @@ -4,13 +4,16 @@ on: push: branches: - main - paths: + paths: &chart_paths - 'src/helm-charts/rosetta-mcp-server/**' - '.github/workflows/publish-mcp-helm-chart.yml' + pull_request: + paths: *chart_paths + workflow_dispatch: jobs: - package-helm-chart: + validate: permissions: contents: read runs-on: ubuntu-latest @@ -23,35 +26,42 @@ jobs: with: version: v3.20.2 - - name: Set environment variables - id: vars + - name: Install helm-unittest plugin + run: helm plugin install https://github.com/helm-unittest/helm-unittest.git --version 0.6.3 + + - name: Lint, template, and unit test chart run: | + CHART_PATH=./src/helm-charts/rosetta-mcp-server + helm lint "$CHART_PATH" + helm template helm-test "$CHART_PATH" >/dev/null + helm unittest "$CHART_PATH" + + publish: + needs: validate + if: github.event_name == 'push' && github.ref_name == 'main' + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + + - name: Set up Helm + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 + with: + version: v3.20.2 + + - name: Package and push Helm chart (OCI / Docker Hub) + run: | + set -euo pipefail CHART_PATH=./src/helm-charts/rosetta-mcp-server VERSION="$(grep -E '^version:' "${CHART_PATH}/Chart.yaml" | awk '{print $2}' | tr -d '"')" CHART_NAME="$(grep -E '^name:' "${CHART_PATH}/Chart.yaml" | awk '{print $2}' | tr -d '"')" REPOSITORY="registry-1.docker.io/griddynamics" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "chart_name=${CHART_NAME}" >> "$GITHUB_OUTPUT" - echo "chart_path=${CHART_PATH}" >> "$GITHUB_OUTPUT" - echo "repository=${REPOSITORY}" >> "$GITHUB_OUTPUT" echo "Package version: ${VERSION}" echo "Chart name: ${CHART_NAME}" echo "OCI registry prefix: oci://${REPOSITORY}" - - - name: Lint and syntax check chart - run: | - helm lint "${{ steps.vars.outputs.chart_path }}" - helm template helm-test "${{ steps.vars.outputs.chart_path }}" >/dev/null - - - name: Package and push Helm chart (OCI / Docker Hub) - if: github.ref_name == 'main' - run: | - set -euo pipefail - CHART="${{ steps.vars.outputs.chart_path }}" - VERSION="${{ steps.vars.outputs.version }}" - CHART_NAME="${{ steps.vars.outputs.chart_name }}" - REPOSITORY="${{ steps.vars.outputs.repository }}" - helm package "$CHART" --version "${VERSION}" + helm package "$CHART_PATH" --version "${VERSION}" PKG="${CHART_NAME}-${VERSION}.tgz" echo "${{ secrets.DOCKERHUB_OAT }}" | helm registry login registry-1.docker.io \ -u "${{ secrets.DOCKERHUB_LOGIN }}" \ diff --git a/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml index 96a3426f..89429914 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml @@ -7,8 +7,6 @@ kind: Middleware metadata: name: {{ include "rosetta-mcp.fullname" . }}-rate-limit namespace: {{ .Release.Namespace }} - annotations: - kubernetes.io/ingress.class: {{ .Values.ingress.className }} labels: {{- include "rosetta-mcp.labels" . | nindent 4 }} spec: diff --git a/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml new file mode 100644 index 00000000..00f5ffb2 --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml @@ -0,0 +1,85 @@ +suite: Deployment +templates: + - deployment.yaml +tests: + - it: creates a Deployment + asserts: + - isKind: + of: Deployment + + - it: sets replicas from replicaCount + set: + replicaCount: 3 + asserts: + - equal: + path: spec.replicas + value: 3 + + - it: uses the configured image + set: + image.repository: my-registry/my-app + image.tag: "1.2.3" + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "my-registry/my-app:1.2.3" + + - it: defaults image tag to Chart.appVersion when tag is not set + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "griddynamics/rosetta-mcp:2.0.0" + + - it: applies pod security context + asserts: + - equal: + path: spec.template.spec.securityContext.runAsNonRoot + value: true + - equal: + path: spec.template.spec.securityContext.runAsUser + value: 1000 + + - it: applies container security context + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation + value: false + - contains: + path: spec.template.spec.containers[0].securityContext.capabilities.drop + content: ALL + + - it: sets resource requests and limits + asserts: + - equal: + path: spec.template.spec.containers[0].resources.requests.cpu + value: 250m + - equal: + path: spec.template.spec.containers[0].resources.requests.memory + value: 512Mi + - equal: + path: spec.template.spec.containers[0].resources.limits.cpu + value: 1000m + - equal: + path: spec.template.spec.containers[0].resources.limits.memory + value: 1Gi + + - it: configures rolling update strategy + asserts: + - equal: + path: spec.strategy.type + value: RollingUpdate + - equal: + path: spec.strategy.rollingUpdate.maxSurge + value: 1 + - equal: + path: spec.strategy.rollingUpdate.maxUnavailable + value: 0 + + - it: renders liveness and readiness probes + asserts: + - isNotNull: + path: spec.template.spec.containers[0].livenessProbe + - isNotNull: + path: spec.template.spec.containers[0].readinessProbe + - isNotNull: + path: spec.template.spec.containers[0].startupProbe diff --git a/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml new file mode 100644 index 00000000..e9257151 --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml @@ -0,0 +1,76 @@ +suite: Ingress +templates: + - ingress.yaml +tests: + - it: creates an Ingress with traefik class by default + asserts: + - isKind: + of: Ingress + - equal: + path: spec.ingressClassName + value: "traefik" + + - it: sets the host from values + set: + ingress.host: myapp.example.com + asserts: + - equal: + path: spec.rules[0].host + value: "myapp.example.com" + + - it: adds rate-limit middleware annotation when traefik rateLimit is configured + asserts: + - isNotNull: + path: metadata.annotations["traefik.ingress.kubernetes.io/router.middlewares"] + - matchRegex: + path: metadata.annotations["traefik.ingress.kubernetes.io/router.middlewares"] + pattern: ".*-rate-limit@kubernetescrd$" + + - it: includes external middlewares in annotation + set: + ingress.traefik.middlewares: + - "traefik-chain-public@kubernetescrd" + asserts: + - matchRegex: + path: metadata.annotations["traefik.ingress.kubernetes.io/router.middlewares"] + pattern: "^traefik-chain-public@kubernetescrd," + + - it: does not render middleware annotation when className is nginx + set: + ingress.className: nginx + ingress.annotations: + nginx.ingress.kubernetes.io/limit-rps: "100" + asserts: + - isNull: + path: metadata.annotations["traefik.ingress.kubernetes.io/router.middlewares"] + - equal: + path: metadata.annotations["nginx.ingress.kubernetes.io/limit-rps"] + value: "100" + + - it: does not set ingressClassName when className is empty + set: + ingress.className: "" + asserts: + - isNull: + path: spec.ingressClassName + + - it: renders TLS when configured + set: + ingress.tls: + - secretName: my-tls + hosts: + - rosetta.example.com + asserts: + - equal: + path: spec.tls[0].secretName + value: my-tls + - equal: + path: spec.tls[0].hosts[0] + value: rosetta.example.com + + - it: is not rendered when ingress is disabled + set: + ingress.enabled: false + asserts: + - hasDocuments: + count: 0 diff --git a/src/helm-charts/rosetta-mcp-server/tests/pdb_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/pdb_test.yaml new file mode 100644 index 00000000..c69d6134 --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/tests/pdb_test.yaml @@ -0,0 +1,27 @@ +suite: PodDisruptionBudget +templates: + - poddisruptionbudget.yaml +tests: + - it: is not rendered when disabled (default) + asserts: + - hasDocuments: + count: 0 + + - it: creates a PDB when enabled with multiple replicas + set: + podDisruptionBudget.enabled: true + replicaCount: 3 + asserts: + - isKind: + of: PodDisruptionBudget + - equal: + path: spec.maxUnavailable + value: 1 + + - it: is not rendered when replicaCount is 1 even if enabled + set: + podDisruptionBudget.enabled: true + replicaCount: 1 + asserts: + - hasDocuments: + count: 0 diff --git a/src/helm-charts/rosetta-mcp-server/tests/service_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/service_test.yaml new file mode 100644 index 00000000..61d39db5 --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/tests/service_test.yaml @@ -0,0 +1,26 @@ +suite: Service +templates: + - service.yaml +tests: + - it: creates a ClusterIP Service + asserts: + - isKind: + of: Service + - equal: + path: spec.type + value: ClusterIP + + - it: configures session affinity for stateful MCP sessions + asserts: + - equal: + path: spec.sessionAffinity + value: ClientIP + - equal: + path: spec.sessionAffinityConfig.clientIP.timeoutSeconds + value: 3600 + + - it: exposes the configured port + asserts: + - equal: + path: spec.ports[0].port + value: 8000 diff --git a/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml new file mode 100644 index 00000000..e20ba70c --- /dev/null +++ b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml @@ -0,0 +1,81 @@ +suite: Traefik Middlewares +templates: + - traefik-middlewares.yaml +tests: + - it: creates rate-limit Middleware when traefik class and rateLimit configured + asserts: + - isKind: + of: Middleware + - equal: + path: apiVersion + value: traefik.io/v1alpha1 + - equal: + path: spec.rateLimit.average + value: 100 + - equal: + path: spec.rateLimit.burst + value: 200 + - equal: + path: spec.rateLimit.period + value: "1s" + + - it: does not have kubernetes.io/ingress.class annotation + asserts: + - isNull: + path: metadata.annotations + + - it: includes chart labels + asserts: + - exists: + path: metadata.labels["helm.sh/chart"] + - exists: + path: metadata.labels["app.kubernetes.io/name"] + + - it: is not rendered when className is nginx + set: + ingress.className: nginx + asserts: + - hasDocuments: + count: 0 + + - it: is not rendered when ingress is disabled + set: + ingress.enabled: false + asserts: + - hasDocuments: + count: 0 + + - it: is not rendered when rateLimit.average is not set + set: + ingress.traefik.rateLimit.average: null + asserts: + - hasDocuments: + count: 0 + + - it: renders with only average when burst and period are empty + set: + ingress.traefik.rateLimit: + average: 50 + burst: 0 + period: "" + asserts: + - equal: + path: spec.rateLimit.average + value: 50 + + - it: renders custom rate-limit values + set: + ingress.traefik.rateLimit: + average: 50 + burst: 100 + period: "10s" + asserts: + - equal: + path: spec.rateLimit.average + value: 50 + - equal: + path: spec.rateLimit.burst + value: 100 + - equal: + path: spec.rateLimit.period + value: "10s" diff --git a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml index f26e7502..5c5be098 100644 --- a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml @@ -7,14 +7,16 @@ image: ingress: host: rosetta-dev.example.com - # Traefik example (uncomment to switch from nginx): - # className: traefik - # annotations: {} - # traefik: - # rateLimit: - # average: 20 - # burst: 200 - # period: "1s" + traefik: + rateLimit: + average: 20 + burst: 200 + period: "1s" + # nginx alternative (uncomment to switch from traefik): + # className: nginx + # annotations: + # nginx.ingress.kubernetes.io/limit-rps: "20" + # nginx.ingress.kubernetes.io/limit-burst-multiplier: "10" env: enabled: true diff --git a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml index fa3a2bce..2161940e 100644 --- a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml @@ -6,14 +6,16 @@ image: ingress: host: rosetta.example.com - # Traefik example (uncomment to switch from nginx): - # className: traefik - # annotations: {} - # traefik: - # rateLimit: - # average: 20 - # burst: 200 - # period: "1s" + traefik: + rateLimit: + average: 100 + burst: 200 + period: "1s" + # nginx alternative (uncomment to switch from traefik): + # className: nginx + # annotations: + # nginx.ingress.kubernetes.io/limit-rps: "100" + # nginx.ingress.kubernetes.io/limit-burst-multiplier: "2" env: enabled: true diff --git a/src/helm-charts/rosetta-mcp-server/values.yaml b/src/helm-charts/rosetta-mcp-server/values.yaml index 0534210b..e15a2e73 100644 --- a/src/helm-charts/rosetta-mcp-server/values.yaml +++ b/src/helm-charts/rosetta-mcp-server/values.yaml @@ -3,30 +3,29 @@ fullnameOverride: "" replicaCount: 1 -# Health probes (optional) -# Example (edit as needed): -# livenessProbe: -# tcpSocket: -# port: port8000 -# periodSeconds: 30 -# timeoutSeconds: 5 -# failureThreshold: 3 - -# readinessProbe: -# httpGet: -# path: /healthz -# port: port8000 -# periodSeconds: 10 -# timeoutSeconds: 5 -# failureThreshold: 3 - -# startupProbe: -# httpGet: -# path: /healthz -# port: port8000 -# periodSeconds: 10 -# timeoutSeconds: 5 -# failureThreshold: 40 +# Health probes +livenessProbe: + tcpSocket: + port: port8000 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + httpGet: + path: /healthz + port: port8000 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + +startupProbe: + httpGet: + path: /healthz + port: port8000 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 40 # PodDisruptionBudget — enable when replicaCount > 1 (or autoscaling minReplicas > 1) podDisruptionBudget: @@ -93,24 +92,31 @@ service: ingress: enabled: true - className: nginx + className: traefik host: rosetta-mcp.local - annotations: - kubernetes.io/ingress.class: nginx + annotations: {} paths: - / - # Traefik-specific settings (only used when className is "traefik"). - # When using Traefik, remove the nginx annotations above and set className to "traefik". + # Traefik-specific settings (applied when className is "traefik"). traefik: # External middleware references (fully-qualified, e.g. "namespace-name@kubernetescrd"). # Rendered first in the middleware chain annotation. middlewares: [] # Per-release rate limiting — creates a Traefik Middleware CRD in the release namespace. - # rateLimit: - # average: 100 - # burst: 200 - # period: "1s" - # Production: enable TLS below (or terminate TLS upstream). Defaults leave MCP on HTTP at the Ingress until configured. + rateLimit: + average: 100 + burst: 200 + period: "1s" + # --- nginx alternative --- + # To use nginx instead of Traefik, set: + # className: nginx + # annotations: + # nginx.ingress.kubernetes.io/proxy-body-size: "10m" + # nginx.ingress.kubernetes.io/limit-rps: "100" + # nginx.ingress.kubernetes.io/limit-burst-multiplier: "2" + # and remove the traefik section above. + # + # TLS: enable below or terminate TLS upstream (e.g. cloud LB, cert-manager). # tls: # - secretName: rosetta-mcp-tls # hosts: From bf98b8b870b046fa9a09c66ec3946697cb0e3274 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Tue, 30 Jun 2026 19:34:57 +0200 Subject: [PATCH 07/12] Update deployment guide to reflect Traefik defaults --- DEPLOYMENT_GUIDE.md | 48 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index fd9468b6..a74bc73f 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -248,35 +248,23 @@ helm install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ 1. **Image** — `image.repository` defaults to `griddynamics/rosetta-mcp`; set `image.tag` or rely on the chart defaulting the tag to [`appVersion`](src/helm-charts/rosetta-mcp-server/Chart.yaml) in the Deployment template. 2. **Rosetta backend** — Set `env.vars` so `ROSETTA_SERVER_URL` resolves to Rosetta Server (in-cluster DNS or ingress URL). 3. **API key** — Supply `ROSETTA_API_KEY` via `env.secrets` (`secretKeyRef`). Create the Kubernetes Secret first or use `eso` to sync it. -4. **Ingress** — Set `ingress.host`. The chart supports both **nginx** (default) and **Traefik** ingress controllers. +4. **Ingress** — Set `ingress.host`. The chart defaults to **Traefik** and also supports **nginx** as an alternative. - **nginx** (default): - - ```yaml - ingress: - className: nginx - host: rosetta.example.com - annotations: - kubernetes.io/ingress.class: nginx - ``` - - **Traefik** — set `className: traefik` and configure middlewares under `ingress.traefik`: + **Traefik** (default) — rate limiting is enabled out of the box: ```yaml ingress: className: traefik host: rosetta.example.com - annotations: {} traefik: rateLimit: - average: 20 + average: 100 burst: 200 period: "1s" ``` - When `rateLimit.average` is set, the chart creates a Traefik `Middleware` CRD - (`-rate-limit`) in the release namespace and wires it into the Ingress - annotation automatically. + The chart creates a Traefik `Middleware` CRD (`-rate-limit`) in the + release namespace and wires it into the Ingress annotation automatically. To reference additional external middlewares (e.g. a platform-wide chain managed outside this chart), list them in `ingress.traefik.middlewares`: @@ -287,13 +275,25 @@ helm install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ middlewares: - "traefik-my-chain@kubernetescrd" rateLimit: - average: 20 + average: 100 burst: 200 period: "1s" ``` External middlewares are rendered first in the annotation, followed by the per-release rate limit. + + **nginx** (alternative) — set `className: nginx` and replace the `traefik` block with nginx annotations: + + ```yaml + ingress: + className: nginx + host: rosetta.example.com + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "10m" + nginx.ingress.kubernetes.io/limit-rps: "100" + nginx.ingress.kubernetes.io/limit-burst-multiplier: "2" + ``` 5. **TLS (production)** — Enable encrypted client traffic before production use. Uncomment and complete the [`ingress.tls`](src/helm-charts/rosetta-mcp-server/values.yaml) block in your overlay so Ingress terminates HTTPS with a TLS `Secret` (or terminate TLS upstream and align hostnames). HTTP-only defaults are unsuitable for production; OAuth and user trust depend on HTTPS. Full environment-variable semantics for OAuth, Redis, analytics, and modes are the same as the application runtime; see [rosetta-mcp-server — Configuration](src/rosetta-mcp-server/README.md#configuration). @@ -320,8 +320,10 @@ helm upgrade --install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ | [`templates/ingress.yaml`](src/helm-charts/rosetta-mcp-server/templates/ingress.yaml) | Optional Ingress | | [`templates/traefik-middlewares.yaml`](src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml) | Traefik Middleware CRDs (when `className: traefik`) | | [`templates/hpa.yaml`](src/helm-charts/rosetta-mcp-server/templates/hpa.yaml) | Optional HPA | +| [`templates/poddisruptionbudget.yaml`](src/helm-charts/rosetta-mcp-server/templates/poddisruptionbudget.yaml) | Optional PDB (when `replicaCount > 1`) | | [`templates/external-secret.yaml`](src/helm-charts/rosetta-mcp-server/templates/external-secret.yaml) | Optional ExternalSecret (`eso.*`) | | [`templates/serviceaccount.yaml`](src/helm-charts/rosetta-mcp-server/templates/serviceaccount.yaml) | ServiceAccount | +| [`tests/`](src/helm-charts/rosetta-mcp-server/tests/) | helm-unittest test suites | #### Deployment characteristics & defaults @@ -346,7 +348,7 @@ sessionAffinityConfig: If `ClientIP` is insufficient behind certain proxies or high fan-out IPs, try ingress affinity on the MCP session header: ```yaml -# NGINX Ingress (optional alternative) +# nginx Ingress (when using className: nginx) nginx.ingress.kubernetes.io/upstream-hash-by: "$http_mcp_session_id" ``` @@ -363,11 +365,11 @@ Base keys in [`src/helm-charts/rosetta-mcp-server/values.yaml`](src/helm-charts/ | `replicaCount` | `1` | Static replicas when HPA disabled | | `autoscaling.enabled` | `false` | HPA toggle | | `ingress.enabled` | `true` | Ingress resource | -| `ingress.className` | `nginx` | Ingress controller (`nginx` or `traefik`) | +| `ingress.className` | `traefik` | Ingress controller (`traefik` or `nginx`) | | `ingress.traefik.middlewares` | `[]` | External Traefik middleware references | -| `ingress.traefik.rateLimit.average` | (unset) | Rate limit — requests per period | -| `ingress.traefik.rateLimit.burst` | (unset) | Rate limit — max burst size | -| `ingress.traefik.rateLimit.period` | (unset) | Rate limit — period (e.g. `"1s"`) | +| `ingress.traefik.rateLimit.average` | `100` | Rate limit — requests per period | +| `ingress.traefik.rateLimit.burst` | `200` | Rate limit — max burst size | +| `ingress.traefik.rateLimit.period` | `"1s"` | Rate limit — period | | `ingress.tls` | Commented in base [`values.yaml`](src/helm-charts/rosetta-mcp-server/values.yaml); enable for production | HTTPS termination at Ingress | | `service.sessionAffinity` | `ClientIP` | Pod stickiness | | `eso.enabled` | `false` | External Secrets Operator sync | From 5245492776b68d0dd38467400daa2c46277dd735 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko <112936003+kkhristenko51@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:37:40 +0200 Subject: [PATCH 08/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../rosetta-mcp-server/tests/traefik_middlewares_test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml index e20ba70c..180c17f8 100644 --- a/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml +++ b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml @@ -19,7 +19,7 @@ tests: path: spec.rateLimit.period value: "1s" - - it: does not have kubernetes.io/ingress.class annotation + - it: does not render any annotations by default asserts: - isNull: path: metadata.annotations From e39634a05e03847a4cbaec17a935061892377910 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Tue, 30 Jun 2026 20:03:59 +0200 Subject: [PATCH 09/12] Fix CRD name normalization, update app version --- src/helm-charts/rosetta-mcp-server/Chart.yaml | 2 +- .../rosetta-mcp-server/templates/traefik-middlewares.yaml | 2 +- .../rosetta-mcp-server/tests/deployment_test.yaml | 4 ++-- .../rosetta-mcp-server/tests/ingress_test.yaml | 8 ++++++++ .../tests/traefik_middlewares_test.yaml | 8 ++++++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/helm-charts/rosetta-mcp-server/Chart.yaml b/src/helm-charts/rosetta-mcp-server/Chart.yaml index d13cf0aa..2d6f552d 100644 --- a/src/helm-charts/rosetta-mcp-server/Chart.yaml +++ b/src/helm-charts/rosetta-mcp-server/Chart.yaml @@ -3,7 +3,7 @@ name: rosetta-mcp-helm-chart description: Helm chart for Rosetta MCP server - the consulting layer between IDEs and Rosetta knowledge base (RAGFlow) type: application version: 0.4.0 -appVersion: "2.0.0" +appVersion: "2.0.25" keywords: - rosetta - mcp diff --git a/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml index 89429914..59176e63 100644 --- a/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml +++ b/src/helm-charts/rosetta-mcp-server/templates/traefik-middlewares.yaml @@ -5,7 +5,7 @@ apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: - name: {{ include "rosetta-mcp.fullname" . }}-rate-limit + name: {{ regexReplaceAll "-{2,}" (printf "%s-rate-limit" (include "rosetta-mcp.fullname" .)) "-" }} namespace: {{ .Release.Namespace }} labels: {{- include "rosetta-mcp.labels" . | nindent 4 }} diff --git a/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml index 00f5ffb2..f3605d6e 100644 --- a/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml +++ b/src/helm-charts/rosetta-mcp-server/tests/deployment_test.yaml @@ -26,9 +26,9 @@ tests: - it: defaults image tag to Chart.appVersion when tag is not set asserts: - - equal: + - matchRegex: path: spec.template.spec.containers[0].image - value: "griddynamics/rosetta-mcp:2.0.0" + pattern: "^griddynamics/rosetta-mcp:.+" - it: applies pod security context asserts: diff --git a/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml index e9257151..3bc8dda1 100644 --- a/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml +++ b/src/helm-charts/rosetta-mcp-server/tests/ingress_test.yaml @@ -74,3 +74,11 @@ tests: asserts: - hasDocuments: count: 0 + + - it: normalizes consecutive dashes in middleware annotation + release: + name: my--app + asserts: + - equal: + path: metadata.annotations["traefik.ingress.kubernetes.io/router.middlewares"] + value: "my-app-rosetta-mcp-rate-limit@kubernetescrd" diff --git a/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml index 180c17f8..ef434fcf 100644 --- a/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml +++ b/src/helm-charts/rosetta-mcp-server/tests/traefik_middlewares_test.yaml @@ -79,3 +79,11 @@ tests: - equal: path: spec.rateLimit.period value: "10s" + + - it: normalizes consecutive dashes in CRD name + release: + name: my--app + asserts: + - equal: + path: metadata.name + value: my-app-rosetta-mcp-rate-limit From d7be15b710fe218b6ecfbc5fe53f727d82c66050 Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko Date: Tue, 30 Jun 2026 20:13:07 +0200 Subject: [PATCH 10/12] Update image tag in examples --- src/helm-charts/rosetta-mcp-server/values-dev.example.yaml | 2 +- src/helm-charts/rosetta-mcp-server/values-prod.example.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml index 5c5be098..12e5cc74 100644 --- a/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-dev.example.yaml @@ -3,7 +3,7 @@ # Replace placeholder hostnames and URLs with your dev environment values. image: - tag: "2.0.0" + tag: "2.0.25" ingress: host: rosetta-dev.example.com diff --git a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml index 2161940e..8e6978c9 100644 --- a/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml +++ b/src/helm-charts/rosetta-mcp-server/values-prod.example.yaml @@ -2,7 +2,7 @@ # Use with: helm install -f values.yaml -f values-prod.yaml ... image: - tag: "2.0.0" + tag: "2.0.25" ingress: host: rosetta.example.com From 0c54122e57e8b6a0ec4821123b8f163ac5e0cbbc Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko <112936003+kkhristenko51@users.noreply.github.com> Date: Tue, 30 Jun 2026 20:14:15 +0200 Subject: [PATCH 11/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/helm-charts/rosetta-mcp-server/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helm-charts/rosetta-mcp-server/values.yaml b/src/helm-charts/rosetta-mcp-server/values.yaml index e15a2e73..032b0414 100644 --- a/src/helm-charts/rosetta-mcp-server/values.yaml +++ b/src/helm-charts/rosetta-mcp-server/values.yaml @@ -102,7 +102,7 @@ ingress: # External middleware references (fully-qualified, e.g. "namespace-name@kubernetescrd"). # Rendered first in the middleware chain annotation. middlewares: [] - # Per-release rate limiting — creates a Traefik Middleware CRD in the release namespace. + # Per-release rate limiting — creates a Traefik Middleware (custom resource) in the release namespace (requires Traefik CRDs installed). rateLimit: average: 100 burst: 200 From b01a446f0dfb73fbff4a30ddd88e03967f0bdafe Mon Sep 17 00:00:00 2001 From: Konstantin Khristenko <112936003+kkhristenko51@users.noreply.github.com> Date: Tue, 30 Jun 2026 20:14:23 +0200 Subject: [PATCH 12/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- DEPLOYMENT_GUIDE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index a74bc73f..a559095e 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -263,8 +263,9 @@ helm install rosetta-mcp ./src/helm-charts/rosetta-mcp-server \ period: "1s" ``` - The chart creates a Traefik `Middleware` CRD (`-rate-limit`) in the - release namespace and wires it into the Ingress annotation automatically. + The chart creates a Traefik `Middleware` custom resource (`-rate-limit`) in the + release namespace and wires it into the Ingress annotation automatically (requires Traefik CRDs + to be installed in the cluster). To reference additional external middlewares (e.g. a platform-wide chain managed outside this chart), list them in `ingress.traefik.middlewares`: