diff --git a/CHANGELOG.md b/CHANGELOG.md index bb090e1a..73dbf9fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + ### Added - **The three upstream lines at their decoupled releases: kagent `1.0.0`, Substrate `1.0.0`, agentgateway `2.0.0` under its nested names** (giantswarm/agent-platform#608; epic giantswarm/giantswarm#37705). The lines release stable semver of their own, decoupled from the upstream versions their `FORK.md`s record, and publish nothing more under the former `vX.Y.Z-gs.N` scheme. `components.kagent*.versionRange` and `components.substrate*.versionRange` are `>=1.0.0 <1.1.0`; both charts name the agentgateway line's `2.0.0` in full — `agentgateway.controller.image` `giantswarm/agentgateway-upstream/controller:2.0.0`, `agentgateway.proxy.image` `giantswarm/agentgateway-upstream/agentgateway:2.0.0`, `substrate.images.agentgateway` the same data plane for Substrate's egress gateway — so the packaging chart's defaults cannot move the images (a value wins over a chart default, and a default that moved alone to the nested names rendered a tag that existed only there); `components.agentgateway.versionRange` is `>=2.2.2 <3.0.0`, the packaging release that renders a bare tag as written. The range shape follows the scheme — a patch of a line never changes a runtime contract, a re-pin onto another upstream release is at least a minor — so `agent-platform.substrate.validateRange` admits an exact version or `>=X.Y.Z =X.Y.Z-gs.N =0.0.30-gs.5 <0.0.31-0`), the BOM pin and, derived from it, the kagent WorkerPool's worker image, so the atelet and the worker are one native, signed release and nothing of the line depends on a copy. The kagent floor moves to `0.11.0-gs.22` the same way (`components.kagent*.versionRange` `>=0.11.0-gs.22 <0.11.1-0`, the BOM pin): the chart paths on gsoci begin with each line's first native release — the older releases exist there as retagger's image copies only, no charts — and the kagent chart's stamped digests of its Go ADK and Claude harness images resolve on gsoci from that release on. With the charts the last ghcr.io reference of the two lines goes: a default installation pulls everything of kagent and Substrate from gsoci, and the dev channel (a `components..semverFilter` on the lines' `-dev.giantswarm.` builds) needs no registry override any more — `kagent.registry` and `substrate.image.registry` stay on their defaults (README "Dev channel"). `make verify-images` tolerates the two lines by name no longer; the upstream CloudNativePG chart is the one pending entry left, behind a Giant Swarm wrapper release on the same operator line. `verify-components` and `verify-kagent-wiring` assert the new sources; `verify-target` holds them on both sides of the golden comparison until `origin/main` carries them. UPGRADE.md. @@ -16,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **The mcp-kubernetes chart's own ServiceMonitor and its three Grafana boards are on** (giantswarm/giantswarm#36711). The chart ships a ServiceMonitor over the dedicated metrics port and the boards `administrator`, `security` and `cluster-operator`, and this chart turned none of them on, so an installation that runs the Kubernetes MCP server collected nothing from it. `mcp-kubernetes.mcpKubernetes.instrumentation.serviceMonitor.enabled` and `mcp-kubernetes.grafanaDashboards.enabled` follow the resolved `global.observability.metrics.serviceMonitor.enabled` (`auto | true | false`), the monitor carries `observability.giantswarm.io/tenant`, and the boards land in `Shared Org / Agent Platform` beside the platform's own. The chart's `prometheusRules` stay off: its three alerts link to runbook pages that do not exist yet. The floor `1.1.1` already carries every key, so no range moves. - **Every agent platform dashboard lands in one folder customers can reach: `Shared Org / Agent Platform`** (giantswarm/giantswarm#36711). The muster board (`Muster / MCP Gateway`) was loaded into the staff-only `Giant Swarm` organization, in a folder named after the component, so the people who run agents on the platform could not see it: `muster.observability.grafanaDashboard.folder` is `Agent Platform` and `.giantswarm.organization` is `Shared Org`. `Shared Org` is the organization every logged-in customer reaches as a Viewer and it carries the observability data of Giant Swarm managed components, which is where these boards belong; the `Giant Swarm` organization is staff-only. The boards that follow — the agentgateway gateway board, the platform's own overview — land in the same folder. - **The agentgateway packaging chart's own monitors and dashboard are on: `agentgateway.monitoring.enabled`** (giantswarm/giantswarm#36711, giantswarm/agentgateway#60). It follows the resolved `global.observability.metrics.serviceMonitor.enabled` like every other monitor of this chart (`auto | true | false`), at the fleet's 60s interval and with the `observability.giantswarm.io/tenant` label on both monitors. It turns on three objects the platform did not have: the **controller** ServiceMonitor, which nothing scraped, and with it the only view of the control plane that serves the data planes their config; upstream's proxy PodMonitor; and upstream's own Grafana board — Overview, Requests, LLM, **MCP tool calls**, Latency, **XDS**, **Runtime** — vendored in the packaging chart since 2.0.0 and never rendered. The MCP and runtime series it reads were already collected and had no consumer. The board is upstream's to maintain, so a version bump carries its fixes; the boards the platform writes itself (LLM cost per agent and per person) stay in `giantswarm/dashboards`. diff --git a/helm/agent-platform/README.md b/helm/agent-platform/README.md index 222e5664..460b4c12 100644 --- a/helm/agent-platform/README.md +++ b/helm/agent-platform/README.md @@ -1257,11 +1257,17 @@ The map is merged into each component's own `nodeSelector` (`muster.nodeSelector | backstage.backstage.extraVolumeMounts[0].mountPath | string | `"/etc/agent-platform/idp-ca"` | | | backstage.backstage.extraVolumeMounts[0].readOnly | bool | `true` | | | mcp-kubernetes.fullnameOverride | string | `"mcp-kubernetes"` | | +| mcp-kubernetes.mcpKubernetes.instrumentation.serviceMonitor.enabled | string | `"auto"` | | +| mcp-kubernetes.mcpKubernetes.instrumentation.serviceMonitor.labels."observability.giantswarm.io/tenant" | string | `"giantswarm"` | | | mcp-kubernetes.mcpKubernetes.oauth.enabled | bool | `true` | | | mcp-kubernetes.mcpKubernetes.oauth.provider | string | `"dex"` | | | mcp-kubernetes.mcpKubernetes.oauth.allowPrivateURLs | bool | `true` | | | mcp-kubernetes.mcpKubernetes.oauth.sso.allowPrivateIPs | bool | `true` | | | mcp-kubernetes.mcpKubernetes.oauth.enableDownstreamOAuth | bool | `true` | | +| mcp-kubernetes.grafanaDashboards.enabled | string | `"auto"` | | +| mcp-kubernetes.grafanaDashboards.folder | string | `"Agent Platform"` | | +| mcp-kubernetes.grafanaDashboards.giantswarm.enabled | bool | `true` | | +| mcp-kubernetes.grafanaDashboards.giantswarm.organization | string | `"Shared Org"` | | | mcp-kubernetes.kubernetesAudience | string | `"dex-k8s-authenticator"` | | | cloudnative-pg | object | `{}` | | | kagent-crds.kmcp.enabled | bool | `false` | | diff --git a/helm/agent-platform/templates/_helpers.tpl b/helm/agent-platform/templates/_helpers.tpl index 6a182485..0625c4fd 100644 --- a/helm/agent-platform/templates/_helpers.tpl +++ b/helm/agent-platform/templates/_helpers.tpl @@ -1317,7 +1317,11 @@ answers, but only where the leaf is left at `auto`: OTLP gateway they export to is part of that platform), agentgateway.monitoring.enabled (the packaging chart's own gate over its controller ServiceMonitor, - proxy PodMonitor and dashboard ConfigMap) + proxy PodMonitor and dashboard ConfigMap), + mcp-kubernetes.mcpKubernetes.instrumentation + .serviceMonitor.enabled and + mcp-kubernetes.grafanaDashboards.enabled (its + monitor and the three boards it ships) Two leaves have no `auto` form and are derived directly, off only: valkey.valkey.metrics.podMonitor.enabled — the valkey chart's own default is on; written false when monitors are off, left absent otherwise so the @@ -1376,6 +1380,8 @@ connectivity release both read the resolved value. */ -}} {{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "muster" "muster" "observability" "grafanaDashboard" "enabled") "value" $monitors) -}} {{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "kagent" "oauth2-proxy" "metrics" "serviceMonitor" "enabled") "value" $monitors) -}} {{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "agentgateway" "monitoring" "enabled") "value" $monitors) -}} +{{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "mcp-kubernetes" "mcpKubernetes" "instrumentation" "serviceMonitor" "enabled") "value" $monitors) -}} +{{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "mcp-kubernetes" "grafanaDashboards" "enabled") "value" $monitors) -}} {{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "kagent" "otel" "tracing" "enabled") "value" $monitors) -}} {{- include "agent-platform.shape.derive" (dict "values" $v "path" (list "kagent" "otel" "logging" "enabled") "value" $monitors) -}} {{- /* valkey PodMonitor: the chart's own default is on, so only "off" is written. */ -}} diff --git a/helm/agent-platform/values.yaml b/helm/agent-platform/values.yaml index 0d26470e..2386bc36 100644 --- a/helm/agent-platform/values.yaml +++ b/helm/agent-platform/values.yaml @@ -4385,6 +4385,17 @@ backstage: # @schema skipProperties: true; additionalProperties: true mcp-kubernetes: # @schema skipProperties: true; additionalProperties: true fullnameOverride: mcp-kubernetes mcpKubernetes: + instrumentation: + # The chart serves /metrics on its own port whatever this holds + # (mcpKubernetes.metrics, on by default); this renders the monitor that + # collects it. `auto` follows the resolved + # global.observability.metrics.serviceMonitor.enabled. + serviceMonitor: + enabled: auto # @schema type: [boolean, string]; enum: [auto, true, false] + labels: + # Mimir routes a scrape by this label; a monitor without it writes to + # no tenant of the GS observability platform. + observability.giantswarm.io/tenant: giantswarm oauth: enabled: true provider: dex @@ -4392,6 +4403,17 @@ mcp-kubernetes: # @schema skipProperties: true; additionalProperties: true sso: allowPrivateIPs: true enableDownstreamOAuth: true + # The three boards the chart ships (administrator, security, cluster + # operator), into the platform's own folder. `auto` follows the resolved + # global.observability.metrics.serviceMonitor.enabled: the operator that picks + # a board up is part of the platform the monitor writes to. Both annotation + # values carry a space, so neither can be a label. + grafanaDashboards: + enabled: auto # @schema type: [boolean, string]; enum: [auto, true, false] + folder: "Agent Platform" + giantswarm: + enabled: true + organization: "Shared Org" # --- Wiring (read by the connectivity chart, not passed to the mcp-kubernetes chart) --- # Audience the forwarded IdP id_token must carry on top of the platform # client's so the kube-apiserver accepts it: muster requests this diff --git a/tests/verify-target.py b/tests/verify-target.py index ecb1b765..19875897 100755 --- a/tests/verify-target.py +++ b/tests/verify-target.py @@ -114,6 +114,19 @@ "--set", "agentgateway.monitoring.grafanaDashboard.annotations.observability\\.giantswarm\\.io/organization=Shared Org", "--set", "agentgateway.monitoring.grafanaDashboard.annotations.observability\\.giantswarm\\.io/folder=Agent Platform", ] +# giantswarm/giantswarm#36711: this tree turns the mcp-kubernetes chart's own +# ServiceMonitor and its three Grafana boards on, which GOLDEN_REF's defaults +# leave off and whose keys it does not carry at all. The whole block is written +# on BOTH sides so the forwarded values compare equal; dropped once GOLDEN_REF +# carries it. +MCP_KUBERNETES_MONITORING_HOLD = [ + "--set", "mcp-kubernetes.mcpKubernetes.instrumentation.serviceMonitor.enabled=false", + "--set", "mcp-kubernetes.mcpKubernetes.instrumentation.serviceMonitor.labels.observability\\.giantswarm\\.io/tenant=giantswarm", + "--set", "mcp-kubernetes.grafanaDashboards.enabled=false", + "--set", "mcp-kubernetes.grafanaDashboards.folder=Agent Platform", + "--set", "mcp-kubernetes.grafanaDashboards.giantswarm.enabled=true", + "--set", "mcp-kubernetes.grafanaDashboards.giantswarm.organization=Shared Org", +] AGENTGATEWAY_IMAGES_HOLD = [ "--set", "agentgateway.controller.image.repository=giantswarm/agentgateway-upstream/controller", "--set", "agentgateway.controller.image.tag=2.0.0", @@ -382,8 +395,8 @@ def check_golden(meta: str, connectivity: str) -> None: # carries the switch. hold_iv = ["--set", "modelServing.imageVerification.enabled=false"] shapes = [ - ("meta default", meta, [*hold_608, *METRIC_LABELS_HOLD, *hold_iv, *MUSTER_DASHBOARD_HOLD, *AGENTGATEWAY_MONITORING_HOLD]), - ("meta ci + engine off", meta, ["-f", f"{meta}/ci/ci-values.yaml", *ENGINE_OFF, *hold_608, *METRIC_LABELS_HOLD, *hold_iv, *MUSTER_DASHBOARD_HOLD, *AGENTGATEWAY_MONITORING_HOLD]), + ("meta default", meta, [*hold_608, *METRIC_LABELS_HOLD, *hold_iv, *MUSTER_DASHBOARD_HOLD, *AGENTGATEWAY_MONITORING_HOLD, *MCP_KUBERNETES_MONITORING_HOLD]), + ("meta ci + engine off", meta, ["-f", f"{meta}/ci/ci-values.yaml", *ENGINE_OFF, *hold_608, *METRIC_LABELS_HOLD, *hold_iv, *MUSTER_DASHBOARD_HOLD, *AGENTGATEWAY_MONITORING_HOLD, *MCP_KUBERNETES_MONITORING_HOLD]), ("connectivity default", connectivity, [*VM, *METRIC_LABELS_HOLD, *hold_iv, *AGENTGATEWAY_IMAGES_HOLD]), ("connectivity full", connectivity, [*CONN_FULL, *METRIC_LABELS_HOLD, *hold_iv, *AGENTGATEWAY_IMAGES_HOLD]), ("connectivity backstage", connectivity, [*CONN_BACKSTAGE, *METRIC_LABELS_HOLD, *hold_iv, *AGENTGATEWAY_IMAGES_HOLD]),