diff --git a/Makefile b/Makefile index 46724eedc..95f9f7d73 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ clean: ## Clean up all build artifacts rm -rf $(CLEAN_TARGETS) .PHONY: build -build: clean tidy format lint ## Build the project +build: clean tidy format ## Build the project go build $(COMMON_BUILD_ARGS) -o $(BINARY_NAME) ./cmd/kubernetes-mcp-server .PHONY: build-multiarch diff --git a/README.md b/README.md index 4a7e90496..b7429c471 100644 --- a/README.md +++ b/README.md @@ -265,16 +265,16 @@ The following sets of tools are available (toolsets marked with ✓ in the Defau -| Toolset | Description | Default | -|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| config | View and manage the current local Kubernetes configuration (kubeconfig) | ✓ | -| core | Most common tools for Kubernetes management (Pods, Generic Resources, Events, etc.) | ✓ | -| helm | Tools for managing Helm charts and releases | | -| kcp | Manage kcp workspaces and multi-tenancy features | | -| kubevirt | KubeVirt virtual machine management tools, check the [KubeVirt documentation](https://github.com/containers/kubernetes-mcp-server/blob/main/docs/kubevirt.md) for more details. | | -| observability | Cluster observability tools for querying Prometheus metrics and Alertmanager alerts | | -| ossm | Most common tools for managing OSSM, check the [OSSM documentation](https://github.com/openshift/openshift-mcp-server/blob/main/docs/OSSM.md) for more details. | | -| tekton | Tekton pipeline management tools for Pipelines, PipelineRuns, Tasks, and TaskRuns. | | +| Toolset | Description | Default | +|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| config | View and manage the current local Kubernetes configuration (kubeconfig) | ✓ | +| core | Most common tools for Kubernetes management (Pods, Generic Resources, Events, etc.) | ✓ | +| helm | Tools for managing Helm charts and releases | | +| kcp | Manage kcp workspaces and multi-tenancy features | | +| kubevirt | KubeVirt virtual machine management tools, check the [KubeVirt documentation](https://github.com/containers/kubernetes-mcp-server/blob/main/docs/kubevirt.md) for more details. | | +| metrics | Toolset for querying Prometheus and Alertmanager endpoints in efficient ways. | | +| ossm | Most common tools for managing OSSM, check the [OSSM documentation](https://github.com/openshift/openshift-mcp-server/blob/main/docs/OSSM.md) for more details. | | +| tekton | Tekton pipeline management tools for Pipelines, PipelineRuns, Tasks, and TaskRuns. | | @@ -459,48 +459,165 @@ In case multi-cluster support is enabled (default) and you have access to multip
-observability - -- **prometheus_query** - Execute an instant PromQL query against the cluster's Thanos Querier. -Returns current metric values at the specified time (or current time if not specified). -Use this for point-in-time metric values. - -Common queries: -- up{job="apiserver"} - Check if API server is up -- sum by(namespace) (container_memory_usage_bytes) - Memory usage by namespace -- rate(container_cpu_usage_seconds_total[5m]) - CPU usage rate -- kube_pod_status_phase{phase="Running"} - Running pods count - - `query` (`string`) **(required)** - PromQL query string (e.g., 'up{job="apiserver"}', 'sum by(namespace) (container_memory_usage_bytes)') - - `time` (`string`) - Optional evaluation timestamp. Accepts RFC3339 format (e.g., '2024-01-01T12:00:00Z') or Unix timestamp. If not provided, uses current time. - -- **prometheus_query_range** - Execute a range PromQL query against the cluster's Thanos Querier. -Returns metric values over a time range with specified resolution. -Use this for time-series data, trends, and historical analysis. - -Supports relative times: -- 'now' for current time -- '-10m', '-1h', '-1d' for relative past times - -Example: Get CPU usage over the last hour with 1-minute resolution. - - `end` (`string`) **(required)** - End time. Accepts RFC3339 timestamp, Unix timestamp, 'now', or relative time - - `query` (`string`) **(required)** - PromQL query string (e.g., 'rate(container_cpu_usage_seconds_total[5m])') - - `start` (`string`) **(required)** - Start time. Accepts RFC3339 timestamp (e.g., '2024-01-01T12:00:00Z'), Unix timestamp, or relative time (e.g., '-1h', '-30m', '-1d') - - `step` (`string`) - Query resolution step width (e.g., '15s', '1m', '5m'). Determines the granularity of returned data points. Default: '1m' - -- **alertmanager_alerts** - Query active and pending alerts from the cluster's Alertmanager. -Useful for monitoring cluster health, detecting issues, and incident response. - -Returns alerts with their labels, annotations, status, and timing information. -Can filter by active/silenced/inhibited state. - -Common use cases: -- Check for critical alerts affecting the cluster -- Monitor for specific alert types (e.g., high CPU, disk pressure) -- Verify alert silences are working correctly - - `active` (`boolean`) - Filter for active (firing) alerts. Default: true - - `filter` (`string`) - Optional filter using Alertmanager filter syntax. Examples: 'alertname=Watchdog', 'severity=critical', 'namespace=openshift-monitoring' - - `inhibited` (`boolean`) - Include inhibited alerts in the results. Default: false - - `silenced` (`boolean`) - Include silenced alerts in the results. Default: false +metrics + +- **list_metrics** - MANDATORY FIRST STEP: List all available metric names in Prometheus. + +YOU MUST CALL THIS TOOL BEFORE ANY OTHER QUERY TOOL + +This tool MUST be called first for EVERY observability question to: +1. Discover what metrics actually exist in this environment +2. Find the EXACT metric name to use in queries +3. Avoid querying non-existent metrics +4. The 'name_regex' parameter should always be provided, and be a best guess of what the metric would be named like. +5. Do not use a blanket regex like .* or .+ in the 'name_regex' parameter. Use specific ones like kube.*, node.*, etc. + +REGEX PATTERN GUIDANCE: +- Prometheus metrics are typically prefixed (e.g., 'prometheus_tsdb_head_series', 'kube_pod_status_phase') +- To match metrics CONTAINING a substring, use wildcards: '.*tsdb.*' matches 'prometheus_tsdb_head_series' +- Without wildcards, the pattern matches EXACTLY: 'tsdb' only matches a metric literally named 'tsdb' (which rarely exists) +- Common patterns: 'kube_pod.*' (pods), '.*memory.*' (memory-related), 'node_.*' (node metrics) +- If you get empty results, try adding '.*' before/after your search term + +NEVER skip this step. NEVER guess metric names. Metric names vary between environments. + +After calling this tool: +1. Search the returned list for relevant metrics +2. Use the EXACT metric name found in subsequent queries +3. If no relevant metric exists, inform the user + - `name_regex` (`string`) **(required)** - Regex pattern to filter metric names. IMPORTANT: Metric names are typically prefixed (e.g., 'prometheus_tsdb_head_series'). Use wildcards to match substrings: '.*tsdb.*' matches any metric containing 'tsdb', while 'tsdb' only matches the exact string 'tsdb'. Examples: 'http_.*' (starts with http_), '.*memory.*' (contains memory), 'node_.*' (starts with node_). This parameter is required. Don't pass in blanket regex like '.*' or '.+'. + +- **execute_instant_query** - Execute a PromQL instant query to get current/point-in-time values. + +PREREQUISITE: You MUST call list_metrics first to verify the metric exists + +WHEN TO USE: +- Current state questions: "What is the current error rate?" +- Point-in-time snapshots: "How many pods are running?" +- Latest values: "Which pods are in Pending state?" + +The 'query' parameter MUST use metric names that were returned by list_metrics. + - `query` (`string`) **(required)** - PromQL query string using metric names verified via list_metrics + - `time` (`string`) - Evaluation time as RFC3339 or Unix timestamp. Omit or use 'NOW' for current time. + +- **execute_range_query** - Execute a PromQL range query to get time-series data over a period. + +PREREQUISITE: You MUST call list_metrics first to verify the metric exists + +WHEN TO USE: +- Trends over time: "What was CPU usage over the last hour?" +- Rate calculations: "How many requests per second?" +- Historical analysis: "Were there any restarts in the last 5 minutes?" + +TIME PARAMETERS: +- 'duration': Look back from now (e.g., "5m", "1h", "24h") +- 'step': Data point resolution (e.g., "1m" for 1-hour duration, "5m" for 24-hour duration) + +The 'query' parameter MUST use metric names that were returned by list_metrics. + - `duration` (`string`) - Duration to look back from now (e.g., '1h', '30m', '1d', '2w') (optional) + - `end` (`string`) - End time as RFC3339 or Unix timestamp (optional). Use `NOW` for current time. + - `query` (`string`) **(required)** - PromQL query string using metric names verified via list_metrics + - `start` (`string`) - Start time as RFC3339 or Unix timestamp (optional) + - `step` (`string`) **(required)** - Query resolution step width (e.g., '15s', '1m', '1h'). Choose based on time range: shorter ranges use smaller steps. + +- **show_timeseries** - Display the results as an interactive timeseries chart. + +This tool works like execute_range_query but renders the results as a visual chart in the UI clients. +Use it when the user wants to see a graph or visualization of time-series data and to use visuals to provide the answer. +Use the show_timeseries as the last tool call after all the other Prometheus tool calls where finalized. + +TIME PARAMETERS: +- 'duration': Look back from now (e.g., "5m", "1h", "24h") +- 'step': Data point resolution (e.g., "1m" for 1-hour duration, "5m" for 24-hour duration) +- 'title': A descriptive chart title (e.g., "API Error Rate Over Last Hour") +- 'description': An explanation of the chart's meaning or context (e.g., "Shows the rate of HTTP 5xx errors per second, broken down by pod") + +The 'query' parameter MUST be a range query and must use metric names that were returned by list_metrics. + - `description` (`string`) - Explanation of the chart's meaning or context (e.g., 'Shows the rate of HTTP 5xx errors per second, broken down by pod'). Displayed below the title when provided. + - `duration` (`string`) - Duration to look back from now (e.g., '1h', '30m', '1d', '2w') (optional) + - `end` (`string`) - End time as RFC3339 or Unix timestamp (optional). Use `NOW` for current time. + - `query` (`string`) **(required)** - PromQL query string using metric names verified via list_metrics + - `start` (`string`) - Start time as RFC3339 or Unix timestamp (optional) + - `step` (`string`) **(required)** - Query resolution step width (e.g., '15s', '1m', '1h'). Choose based on time range: shorter ranges use smaller steps. + - `title` (`string`) - Human-readable chart title describing what the query shows (e.g., 'API Error Rate Over Last Hour'). Displayed above the chart when provided. + +- **get_label_names** - Get all label names (dimensions) available for filtering a metric. + +WHEN TO USE (after calling list_metrics): +- To discover how to filter metrics (by namespace, pod, service, etc.) +- Before constructing label matchers in PromQL queries + +The 'metric' parameter should use a metric name from list_metrics output. + - `end` (`string`) - End time for label discovery as RFC3339 or Unix timestamp (optional, defaults to now) + - `metric` (`string`) - Metric name (from list_metrics) to get label names for. Leave empty for all metrics. + - `start` (`string`) - Start time for label discovery as RFC3339 or Unix timestamp (optional, defaults to 1 hour ago) + +- **get_label_values** - Get all unique values for a specific label. + +WHEN TO USE (after calling list_metrics and get_label_names): +- To find exact label values for filtering (namespace names, pod names, etc.) +- To see what values exist before constructing queries + +The 'metric' parameter should use a metric name from list_metrics output. + - `end` (`string`) - End time for label value discovery as RFC3339 or Unix timestamp (optional, defaults to now) + - `label` (`string`) **(required)** - Label name (from get_label_names) to get values for + - `metric` (`string`) - Metric name (from list_metrics) to scope the label values to. Leave empty for all metrics. + - `start` (`string`) - Start time for label value discovery as RFC3339 or Unix timestamp (optional, defaults to 1 hour ago) + +- **get_series** - Get time series matching selectors and preview cardinality. + +WHEN TO USE (optional, after calling list_metrics): +- To verify label filters match expected series before querying +- To check cardinality and avoid slow queries + +CARDINALITY GUIDANCE: +- <100 series: Safe +- 100-1000: Usually fine +- >1000: Add more label filters + +The selector should use metric names from list_metrics output. + - `end` (`string`) - End time for series discovery as RFC3339 or Unix timestamp (optional, defaults to now) + - `matches` (`string`) **(required)** - PromQL series selector using metric names from list_metrics + - `start` (`string`) - Start time for series discovery as RFC3339 or Unix timestamp (optional, defaults to 1 hour ago) + +- **get_alerts** - Get alerts from Alertmanager. + +WHEN TO USE: +- START HERE when investigating issues: if the user asks about things breaking, errors, failures, outages, services being down, or anything going wrong in the cluster +- When the user mentions a specific alert name - use this tool to get the alert's full labels (namespace, pod, service, etc.) which are essential for further investigation with other tools +- To see currently firing alerts in the cluster +- To check which alerts are active, silenced, or inhibited +- To understand what's happening before diving into metrics or logs + +INVESTIGATION TIP: Alert labels often contain the exact identifiers (pod names, namespaces, job names) needed for targeted queries with prometheus tools. + +FILTERING: +- Use 'active' to filter for only active alerts (not resolved) +- Use 'silenced' to filter for silenced alerts +- Use 'inhibited' to filter for inhibited alerts +- Use 'filter' to apply label matchers (e.g., "alertname=HighCPU") +- Use 'receiver' to filter alerts by receiver name + +All filter parameters are optional. Without filters, all alerts are returned. + - `active` (`boolean`) - Filter for active alerts only (true/false, optional) + - `filter` (`string`) - Label matchers to filter alerts (e.g., 'alertname=HighCPU', optional) + - `inhibited` (`boolean`) - Filter for inhibited alerts only (true/false, optional) + - `receiver` (`string`) - Receiver name to filter alerts (optional) + - `silenced` (`boolean`) - Filter for silenced alerts only (true/false, optional) + - `unprocessed` (`boolean`) - Filter for unprocessed alerts only (true/false, optional) + +- **get_silences** - Get silences from Alertmanager. + +WHEN TO USE: +- To see which alerts are currently silenced +- To check active, pending, or expired silences +- To investigate why certain alerts are not firing notifications + +FILTERING: +- Use 'filter' to apply label matchers to find specific silences + +Silences are used to temporarily mute alerts based on label matchers. This tool helps you understand what is currently silenced in your environment. + - `filter` (`string`) - Label matchers to filter silences (e.g., 'alertname=HighCPU', optional)
diff --git a/docs/METRICS.md b/docs/METRICS.md new file mode 100644 index 000000000..9c4967958 --- /dev/null +++ b/docs/METRICS.md @@ -0,0 +1,285 @@ +# Metrics Toolset (`metrics`) + +This toolset provides tools for querying Prometheus/Thanos metrics and Alertmanager alerts. +It is implemented by the [`rhobs/obs-mcp`](https://github.com/rhobs/obs-mcp) package and registered +into the openshift-mcp-server as the `metrics` toolset. + +## Tools + +### list_metrics + +**First step for metric discovery.** Lists available metric names using a regex filter. +Always start here before writing PromQL queries to discover what metrics exist. + +**Parameters:** +- `name_regex` (string, required) — Regex pattern to filter metric names (e.g., `"apiserver"`, `"container_cpu"`) + +**Example:** `name_regex: "kube_pod"` + +--- + +### execute_instant_query + +Execute a PromQL instant query to get point-in-time values. + +**Parameters:** +- `query` (string, required) — PromQL query string +- `time` (string, optional) — Evaluation time. Accepts RFC3339 (`2024-01-15T10:00:00Z`), Unix timestamp, or relative (`-5m`, `now`) + +**Example:** `query: up{job="apiserver"}` + +--- + +### execute_range_query + +Execute a PromQL range query for time-series data. + +**Parameters:** +- `query` (string, required) — PromQL query string +- `step` (string, required) — Query resolution step (e.g., `15s`, `1m`, `1h`) +- `start` (string, optional) — Start time (RFC3339, Unix, or relative like `-1h`) +- `end` (string, optional) — End time (RFC3339, Unix, or relative like `now`) +- `duration` (string, optional) — Duration to look back instead of explicit start/end (e.g., `1h`, `30m`) + +**Example:** +``` +query: rate(container_cpu_usage_seconds_total[5m]) +duration: 1h +step: 1m +``` + +--- + +### show_timeseries + +Display range query results as an interactive timeseries chart (requires a compatible UI). +Accepts the same parameters as `execute_range_query`, plus: + +- `title` (string, optional) — Chart title +- `description` (string, optional) — Chart description + +--- + +### get_label_names + +Get all label names (dimensions) available for a metric or across all metrics. + +**Parameters:** +- `metric` (string, optional) — Metric name to scope results to +- `start` (string, optional) — Start time (defaults to 1 hour ago) +- `end` (string, optional) — End time (defaults to now) + +--- + +### get_label_values + +Get all unique values for a specific label. + +**Parameters:** +- `label` (string, required) — Label name to get values for +- `metric` (string, optional) — Metric name to scope results to +- `start` (string, optional) — Start time +- `end` (string, optional) — End time + +--- + +### get_series + +Get time series matching a selector and preview cardinality before running expensive queries. + +**Parameters:** +- `matches` (string, required) — PromQL series selector (e.g., `container_cpu_usage_seconds_total{namespace="default"}`) +- `start` (string, optional) — Start time +- `end` (string, optional) — End time + +--- + +### get_alerts + +Query alerts from Alertmanager. Requires `alertmanager_url` to be configured. + +**Parameters:** +- `active` (boolean, optional) — Filter for active alerts only +- `silenced` (boolean, optional) — Filter for silenced alerts +- `inhibited` (boolean, optional) — Filter for inhibited alerts +- `unprocessed` (boolean, optional) — Filter for unprocessed alerts +- `filter` (string, optional) — Label matchers (e.g., `alertname=HighCPU,severity=critical`) +- `receiver` (string, optional) — Receiver name to filter by + +--- + +### get_silences + +Query silences from Alertmanager. Requires `alertmanager_url` to be configured. + +**Parameters:** +- `filter` (string, optional) — Label matchers to filter silences + +--- + +## Enable the Toolset + +### Command line + +```bash +kubernetes-mcp-server --toolsets core,metrics +``` + +### Configuration file (TOML) + +```toml +toolsets = ["core", "metrics"] +``` + +### MCP client configuration + +```json +{ + "mcpServers": { + "kubernetes": { + "command": "npx", + "args": ["-y", "kubernetes-mcp-server@latest", "--toolsets", "core,metrics"] + } + } +} +``` + +--- + +## Configuration + +The toolset is configured via a `[metrics]` section in the TOML config file. + +```toml +[toolset_configs.metrics] +# URL of the Prometheus or Thanos Querier endpoint. +# Required for metric/query tools. Defaults to http://localhost:9090 if unset. +# Example for OpenShift in-cluster Thanos: +prometheus_url = "https://thanos-querier.openshift-monitoring.svc.cluster.local:9091" +# Example for an external route: +# prometheus_url = "https://thanos-querier-openshift-monitoring.apps.example.com" + +# URL of the Alertmanager endpoint. +# Required for get_alerts and get_silences. No default — those tools fail without it. +# Example for OpenShift in-cluster Alertmanager: +alertmanager_url = "https://alertmanager-main.openshift-monitoring.svc.cluster.local:9095" +# Example for an external route: +# alertmanager_url = "https://alertmanager-main-openshift-monitoring.apps.example.com" + +# Skip TLS certificate verification. +# Set to true only for development or when using a self-signed cert you can't import. +# Default: false +insecure = false + +# Query safety guardrails. +# Valid values: +# "all" — enable all guardrails (default) +# "none" — disable all guardrails +# or a comma-separated subset: +# "disallow-explicit-name-label" — reject queries that filter by __name__ label directly +# "require-label-matcher" — reject queries with no label matchers (full table scans) +# "disallow-blanket-regex" — reject .* regexes that would match too many series +# Default: "all" +guardrails = "none" + +# Maximum number of series allowed per metric before a query is rejected. +# Guards against accidentally pulling millions of series. +# Set to 0 to disable this check. +# Default: 20000 +max_metric_cardinality = 0 + +# Maximum number of label values allowed before a blanket regex (.*) is rejected. +# Only applies when the "disallow-blanket-regex" guardrail is active. +# Set to 0 to always disallow blanket regex regardless of cardinality. +# Default: 500 +max_label_cardinality = 0 + +# Controls whether range queries return full data points +# instead of summary statistics to the model. +# Default: false (return summary statistics) +range_query_full_response = false +``` + +### Configuration reference + +| Option | Type | Default | Description | +|---|---|---|---| +| `prometheus_url` | string | `http://localhost:9090` | Prometheus or Thanos Querier endpoint URL | +| `alertmanager_url` | string | — | Alertmanager endpoint URL (required for alert/silence tools) | +| `insecure` | bool | `false` | Skip TLS certificate verification | +| `guardrails` | string | `"all"` | Query safety checks (`"all"`, `"none"`, or comma-separated list) | +| `max_metric_cardinality` | uint64 | `20000` | Max series per metric (0 = disabled) | +| `max_label_cardinality` | uint64 | `500` | Max label values before blanket regex is rejected (0 = always reject) | +| `range_query_full_response` | bool | `false` | Controls whether range queries return full data points | + +--- + +## Authentication and TLS + +The toolset reuses the bearer token from the active Kubernetes/OpenShift kubeconfig (or the +in-cluster service account token when running inside a pod). No separate credentials are needed. + +Currently there's a limitation here, as kubeconfig using client certificates, will get a 401 from Thanos Querier. +So users need to `oc login` first to get a token-based session. + +**TLS certificate resolution order:** + +1. CA certificate embedded in the kubeconfig (`certificate-authority-data`) +2. Service account CA file at `/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt` + (automatically present when running in a pod on OpenShift) +3. System certificate pool (for publicly trusted certificates) + +For endpoints with self-signed or private certificates that are not covered by the above, set +`insecure = true` in the config (not recommended in production). + +--- + +## Endpoint Discovery + +`obs-mcp` does not perform automatic OpenShift route discovery. Endpoint URLs must be provided explicitly via `prometheus_url` and `alertmanager_url`. + +Typical values for OpenShift: + +| Endpoint | In-cluster service URL | External route URL (example) | +|---|---|---| +| Thanos Querier | `https://thanos-querier.openshift-monitoring.svc.cluster.local:9091` | `https://thanos-querier-openshift-monitoring.apps.` | +| Alertmanager | `https://alertmanager-main.openshift-monitoring.svc.cluster.local:9095` | `https://alertmanager-main-openshift-monitoring.apps.` | + +To look up the actual routes in an OpenShift cluster: + +```bash +oc get routes -n openshift-monitoring +``` + +--- + +## Prerequisites + +- **Prometheus or Thanos Querier** accessible at `prometheus_url` +- **Alertmanager** accessible at `alertmanager_url` (only required for `get_alerts` / `get_silences`) +- **Bearer token** with read access to the metrics endpoints (automatically sourced from kubeconfig + or in-cluster service account) +- For OpenShift in-cluster use, the service account needs: + ``` + cluster-monitoring-view (ClusterRole) + ``` + +--- + +## Query Guardrails + +Guardrails protect against runaway queries that could overload the metrics backend. +They are enabled by default (`guardrails = "all"`). + +| Guardrail | Key | Effect | +|---|---|---| +| Disallow explicit name label | `disallow-explicit-name-label` | Rejects queries that filter on `__name__` directly instead of using metric name syntax | +| Require label matcher | `require-label-matcher` | Rejects bare metric name queries with no label filters (prevents full-cardinality scans) | +| Disallow blanket regex | `disallow-blanket-regex` | Rejects `.*` or equivalently broad regex matchers when the matched series count exceeds `max_label_cardinality` | + +To disable a specific guardrail while keeping others: + +```toml +[toolset_configs.metrics] +guardrails = "disallow-explicit-name-label,require-label-matcher" # omit disallow-blanket-regex +``` \ No newline at end of file diff --git a/docs/OBSERVABILITY.md b/docs/OBSERVABILITY.md deleted file mode 100644 index 71d49372a..000000000 --- a/docs/OBSERVABILITY.md +++ /dev/null @@ -1,247 +0,0 @@ -# Observability Toolset - -This toolset provides tools for querying OpenShift cluster observability data including Prometheus metrics and Alertmanager alerts. - -## Tools - -### prometheus_query - -Execute instant PromQL queries against the cluster's Thanos Querier. - -**Parameters:** -- `query` (required) - PromQL query string -- `time` (optional) - Evaluation timestamp (RFC3339, Unix timestamp, or relative like `-5m`, `now`) - -**Example:** -``` -Query: up{job="apiserver"} -``` - -### prometheus_query_range - -Execute range PromQL queries for time-series data. - -**Parameters:** -- `query` (required) - PromQL query string -- `start` (required) - Start time (RFC3339, Unix timestamp, or relative like `-1h`) -- `end` (required) - End time (RFC3339, Unix timestamp, or relative like `now`) -- `step` (optional) - Query resolution step (default: `1m`) - -**Example:** -``` -Query: rate(container_cpu_usage_seconds_total[5m]) -Start: -1h -End: now -Step: 1m -``` - -### alertmanager_alerts - -Query alerts from the cluster's Alertmanager. - -**Parameters:** -- `active` (optional) - Include active alerts (default: true) -- `silenced` (optional) - Include silenced alerts (default: false) -- `inhibited` (optional) - Include inhibited alerts (default: false) -- `filter` (optional) - Label filter in PromQL format (e.g., `alertname="Watchdog"`) - -**Example:** -``` -Active: true -Filter: severity="critical" -``` - -## Enable the Observability Toolset - -### Option 1: Command Line - -```bash -kubernetes-mcp-server --toolsets core,config,helm,observability -``` - -### Option 2: Configuration File - -```toml -toolsets = ["core", "config", "helm", "observability"] -``` - -### Option 3: MCP Client Configuration - -```json -{ - "mcpServers": { - "kubernetes": { - "command": "npx", - "args": ["-y", "kubernetes-mcp-server@latest", "--toolsets", "core,config,helm,observability"] - } - } -} -``` - -## Configuration - -The observability toolset supports optional configuration via the config file: - -```toml -[observability] -# Custom monitoring namespace (default: "openshift-monitoring") -monitoring_namespace = "custom-monitoring" -``` - -| Option | Default | Description | -|--------|---------|-------------| -| `monitoring_namespace` | `openshift-monitoring` | Namespace where Prometheus and Alertmanager routes are located | - -## Prerequisites - -The observability tools require: - -1. **OpenShift cluster** - These tools are designed for OpenShift and rely on OpenShift-specific routes -2. **Monitoring stack enabled** - The cluster must have the monitoring stack deployed (default in OpenShift) -3. **Proper RBAC** - The user/service account must have permissions to: - - Read routes in `openshift-monitoring` namespace - - Access the Thanos Querier and Alertmanager APIs - -## How It Works - -### Route Discovery - -The tools automatically discover the Prometheus (Thanos Querier) and Alertmanager endpoints by reading OpenShift routes: - -- **Thanos Querier**: `thanos-querier` route in `openshift-monitoring` namespace -- **Alertmanager**: `alertmanager-main` route in `openshift-monitoring` namespace - -### Authentication - -The tools use the bearer token from your Kubernetes configuration to authenticate with the monitoring endpoints. This is the same credential used to access the cluster. - -### Relative Time Support - -Time parameters support multiple formats: - -| Format | Example | Description | -|--------|---------|-------------| -| RFC3339 | `2024-01-15T10:00:00Z` | Absolute timestamp | -| Unix | `1705312800` | Unix timestamp in seconds | -| Relative | `-10m`, `-1h`, `-1d` | Relative to current time | -| Keyword | `now` | Current time | - -## Security Considerations - -### Allowed Prometheus Endpoints - -Only read-only Prometheus API endpoints are allowed: -- `/api/v1/query` - Instant queries -- `/api/v1/query_range` - Range queries -- `/api/v1/series` - Series metadata -- `/api/v1/labels` - Label names -- `/api/v1/label//values` - Label values - -Administrative endpoints (like `/api/v1/admin/*`) are blocked. - -### Allowed Alertmanager Endpoints - -Only alert query endpoints are allowed: -- `/api/v2/alerts` - List alerts -- `/api/v2/silences` - List silences -- `/api/v1/alerts` - Legacy alert endpoint - -### Query Limits - -- Maximum query length: 10,000 characters -- Maximum response size: 10MB - -## Common Use Cases - -### Cluster Health - -**Check if all API servers are up:** -``` -Query: up{job="apiserver"} -``` - -**API server request latency (99th percentile):** -``` -Query: histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket[5m])) by (le, verb)) -``` - -### Node and Pod Metrics - -**Node CPU usage percentage:** -``` -Query: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) -``` - -**Pods in CrashLoopBackOff:** -``` -Query: kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} > 0 -``` - -**Container memory usage by namespace:** -``` -Query: sum by(namespace) (container_memory_working_set_bytes{container!=""}) -``` - -### Alerting - -**Get all firing critical alerts:** -``` -Tool: alertmanager_alerts -Active: true -Filter: severity="critical" -``` - -**Count alerts by severity:** -``` -Query: count by(severity) (ALERTS{alertstate="firing"}) -``` - -### Network - -**Network receive rate by pod:** -``` -Query: rate(container_network_receive_bytes_total[5m]) -Start: -1h -End: now -Step: 1m -``` - -### etcd Health - -**etcd leader changes:** -``` -Query: changes(etcd_server_leader_changes_seen_total[1h]) -``` - -**etcd disk sync duration:** -``` -Query: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) -``` - -## Troubleshooting - -### "failed to get route" Error - -The monitoring routes may not exist or the user lacks permissions: -```bash -oc get routes -n openshift-monitoring -``` - -### "no bearer token available" Error - -Ensure your kubeconfig has a valid token: -```bash -oc whoami -oc get pods -n openshift-monitoring -``` - -### Empty Results from Prometheus - -Verify the query works in the OpenShift console: -1. Go to **Observe** > **Metrics** -2. Enter your PromQL query -3. Check for results - -### TLS Certificate Errors - -The tools use `InsecureSkipVerify` for route access. If you need strict TLS verification, this would require additional configuration. diff --git a/docs/configuration.md b/docs/configuration.md index 487a02631..d6e20a263 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -288,16 +288,16 @@ Toolsets group related tools together. Enable only the toolsets you need to redu -| Toolset | Description | Default | -|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| config | View and manage the current local Kubernetes configuration (kubeconfig) | ✓ | -| core | Most common tools for Kubernetes management (Pods, Generic Resources, Events, etc.) | ✓ | -| helm | Tools for managing Helm charts and releases | | -| kcp | Manage kcp workspaces and multi-tenancy features | | -| kubevirt | KubeVirt virtual machine management tools, check the [KubeVirt documentation](https://github.com/containers/kubernetes-mcp-server/blob/main/docs/kubevirt.md) for more details. | | -| observability | Cluster observability tools for querying Prometheus metrics and Alertmanager alerts | | -| ossm | Most common tools for managing OSSM, check the [OSSM documentation](https://github.com/openshift/openshift-mcp-server/blob/main/docs/OSSM.md) for more details. | | -| tekton | Tekton pipeline management tools for Pipelines, PipelineRuns, Tasks, and TaskRuns. | | +| Toolset | Description | Default | +|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| config | View and manage the current local Kubernetes configuration (kubeconfig) | ✓ | +| core | Most common tools for Kubernetes management (Pods, Generic Resources, Events, etc.) | ✓ | +| helm | Tools for managing Helm charts and releases | | +| kcp | Manage kcp workspaces and multi-tenancy features | | +| kubevirt | KubeVirt virtual machine management tools, check the [KubeVirt documentation](https://github.com/containers/kubernetes-mcp-server/blob/main/docs/kubevirt.md) for more details. | | +| metrics | Toolset for querying Prometheus and Alertmanager endpoints in efficient ways. | | +| ossm | Most common tools for managing OSSM, check the [OSSM documentation](https://github.com/openshift/openshift-mcp-server/blob/main/docs/OSSM.md) for more details. | | +| tekton | Tekton pipeline management tools for Pipelines, PipelineRuns, Tasks, and TaskRuns. | | diff --git a/go.mod b/go.mod index e2167178d..aea3b37ac 100644 --- a/go.mod +++ b/go.mod @@ -11,9 +11,10 @@ require ( github.com/google/gnostic-models v0.7.1 github.com/google/jsonschema-go v0.4.2 github.com/google/uuid v1.6.0 - github.com/miekg/dns v1.1.57 + github.com/miekg/dns v1.1.68 github.com/modelcontextprotocol/go-sdk v1.5.0 github.com/prometheus/client_golang v1.23.2 + github.com/rhobs/obs-mcp v0.1.0-rc.2.0.20260416152558-4214041d479a github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 @@ -66,11 +67,12 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.3 // indirect github.com/containerd/containerd v1.7.30 // indirect - github.com/containerd/errdefs v0.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dennwc/varint v1.0.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect @@ -80,15 +82,36 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-openapi/analysis v0.24.1 // indirect + github.com/go-openapi/errors v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.22.1 // indirect + github.com/go-openapi/jsonreference v0.21.3 // indirect + github.com/go-openapi/loads v0.23.2 // indirect + github.com/go-openapi/runtime v0.29.2 // indirect + github.com/go-openapi/spec v0.22.1 // indirect + github.com/go-openapi/strfmt v0.25.0 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-openapi/validate v0.25.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.27.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gosuri/uitable v0.0.4 // indirect + github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -96,14 +119,13 @@ require ( github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.4 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -116,16 +138,20 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/alertmanager v0.30.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/otlptranslator v1.0.0 // indirect github.com/prometheus/procfs v0.20.1 // indirect + github.com/prometheus/prometheus v0.307.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rubenv/sql-migrate v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -138,13 +164,15 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + go.mongodb.org/mongo-driver v1.17.6 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/exp v0.0.0-20250808145144-a408d31f581a // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/net v0.52.0 // indirect golang.org/x/sys v0.42.0 // indirect diff --git a/go.sum b/go.sum index bf7e6a94d..dad362fba 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,29 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= +cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= @@ -22,12 +38,44 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= +github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= +github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -48,16 +96,16 @@ github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg= github.com/containerd/containerd v1.7.30 h1:/2vezDpLDVGGmkUXmlNPLCCNKHJ5BbC5tJB5JNzQhqE= github.com/containerd/containerd v1.7.30/go.mod h1:fek494vwJClULlTpExsmOyKCMUAbuVjlFsJQc4/j44M= -github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= -github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A= github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -67,6 +115,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM= @@ -114,22 +164,68 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM= +github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84= +github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= +github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= +github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= +github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY= +github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0= +github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0= +github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k= +github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA= +github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= +github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw= +github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= @@ -141,10 +237,16 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= +github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -153,6 +255,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 h1:cLN4IBkmkYZNnk7EAJ0BHIethd+J6LqxFNw5mSiI2bM= +github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= @@ -166,16 +270,16 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= -github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= -github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= @@ -196,8 +300,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -206,8 +308,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -230,8 +332,14 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= @@ -244,6 +352,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -251,6 +361,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +github.com/prometheus/alertmanager v0.30.1 h1:427prmCHuy1rMmV7fl/TVQFh5A/78XQ/Mp+TsswZNGM= +github.com/prometheus/alertmanager v0.30.1/go.mod h1:93PBumcTLr/gNtNtM0m7BcCffbvYP5bKuLBWiOnISaA= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -261,12 +373,18 @@ github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEo github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/prometheus/prometheus v0.307.3 h1:zGIN3EpiKacbMatcUL2i6wC26eRWXdoXfNPjoBc2l34= +github.com/prometheus/prometheus v0.307.3/go.mod h1:sPbNW+KTS7WmzFIafC3Inzb6oZVaGLnSvwqTdz2jxRQ= +github.com/prometheus/sigv4 v0.3.0 h1:QIG7nTbu0JTnNidGI1Uwl5AGVIChWUACxn2B/BQ1kms= +github.com/prometheus/sigv4 v0.3.0/go.mod h1:fKtFYDus2M43CWKMNtGvFNHGXnAJJEGZbiYCmVp/F8I= github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/rhobs/obs-mcp v0.1.0-rc.2.0.20260416152558-4214041d479a h1:bVZQWj4Bb85dsCQnLDNHTZZU92XcbmAmYjn6QseeN9g= +github.com/rhobs/obs-mcp v0.1.0-rc.2.0.20260416152558-4214041d479a/go.mod h1:Zuq0El4CL3JEcFCRyKqODua0Fi3LYojljTqQkGGdfJ8= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -313,6 +431,8 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= @@ -345,8 +465,8 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlP go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs= -go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= -go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= @@ -359,6 +479,8 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09 go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -371,8 +493,8 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/exp v0.0.0-20250808145144-a408d31f581a h1:Y+7uR/b1Mw2iSXZ3G//1haIiSElDQZ8KWh0h+sZPG90= +golang.org/x/exp v0.0.0-20250808145144-a408d31f581a/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= @@ -397,6 +519,8 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= +google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= diff --git a/internal/tools/update-readme/main.go b/internal/tools/update-readme/main.go index e34add736..aa10dcbb2 100644 --- a/internal/tools/update-readme/main.go +++ b/internal/tools/update-readme/main.go @@ -19,8 +19,8 @@ import ( _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kcp" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kiali" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kubevirt" - _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/observability" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/tekton" + _ "github.com/rhobs/obs-mcp/pkg/toolset" ) type OpenShift struct{} diff --git a/pkg/mcp/modules.go b/pkg/mcp/modules.go index d0d70ddfe..2e1a97ca8 100644 --- a/pkg/mcp/modules.go +++ b/pkg/mcp/modules.go @@ -8,7 +8,7 @@ import ( _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kiali" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kubevirt" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/netedge" - _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/observability" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/openshift" _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/tekton" + _ "github.com/rhobs/obs-mcp/pkg/toolset" ) diff --git a/pkg/prometheus/alertmanager.go b/pkg/prometheus/alertmanager.go deleted file mode 100644 index 1b06941e1..000000000 --- a/pkg/prometheus/alertmanager.go +++ /dev/null @@ -1,40 +0,0 @@ -package prometheus - -import ( - "context" - "encoding/json" - "fmt" - "net/url" -) - -// buildAlertsParams constructs query parameters for Alertmanager alerts API. -func buildAlertsParams(active, silenced, inhibited bool, filter string) url.Values { - params := url.Values{} - params.Set("active", fmt.Sprintf("%t", active)) - params.Set("silenced", fmt.Sprintf("%t", silenced)) - params.Set("inhibited", fmt.Sprintf("%t", inhibited)) - if filter != "" { - params.Add("filter", filter) - } - return params -} - -// GetAlerts retrieves alerts from Alertmanager. -func (c *Client) GetAlerts(ctx context.Context, active, silenced, inhibited bool, filter string) ([]Alert, error) { - body, err := c.executeRequest(ctx, "/api/v2/alerts", buildAlertsParams(active, silenced, inhibited, filter)) - if err != nil { - return nil, err - } - - var alerts []Alert - if err := json.Unmarshal(body, &alerts); err != nil { - return nil, fmt.Errorf("failed to parse alerts response: %w", err) - } - - return alerts, nil -} - -// GetAlertsRaw retrieves raw JSON alerts from Alertmanager. -func (c *Client) GetAlertsRaw(ctx context.Context, active, silenced, inhibited bool, filter string) ([]byte, error) { - return c.executeRequest(ctx, "/api/v2/alerts", buildAlertsParams(active, silenced, inhibited, filter)) -} diff --git a/pkg/prometheus/client_test.go b/pkg/prometheus/client_test.go index 1fed6efd9..ae82ada50 100644 --- a/pkg/prometheus/client_test.go +++ b/pkg/prometheus/client_test.go @@ -237,163 +237,6 @@ func (s *PrometheusSuite) TestQueryRange() { }) } -func (s *PrometheusSuite) TestGetAlerts() { - s.Run("retrieves alerts with parameters", func() { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - s.Equal("/api/v2/alerts", r.URL.Path) - s.Equal("true", r.URL.Query().Get("active")) - s.Equal("false", r.URL.Query().Get("silenced")) - s.Equal("false", r.URL.Query().Get("inhibited")) - - alerts := []Alert{ - { - Labels: map[string]string{"alertname": "HighCPU", "severity": "warning"}, - Annotations: map[string]string{"summary": "CPU usage is high"}, - StartsAt: "2024-01-01T00:00:00Z", - Status: AlertStatus{ - State: "active", - }, - }, - } - _ = json.NewEncoder(w).Encode(alerts) - })) - defer server.Close() - - client := NewClient(server.URL) - alerts, err := client.GetAlerts(context.Background(), true, false, false, "") - - s.NoError(err) - s.Len(alerts, 1) - s.Equal("HighCPU", alerts[0].Labels["alertname"]) - s.Equal("active", alerts[0].Status.State) - }) - - s.Run("includes filter parameter when specified", func() { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - s.Equal("alertname=Watchdog", r.URL.Query().Get("filter")) - - alerts := []Alert{} - _ = json.NewEncoder(w).Encode(alerts) - })) - defer server.Close() - - client := NewClient(server.URL) - _, err := client.GetAlerts(context.Background(), true, false, false, "alertname=Watchdog") - - s.NoError(err) - }) -} - -func (s *PrometheusSuite) TestConvertRelativeTime() { - s.Run("handles 'now' keyword", func() { - before := time.Now().UTC() - result, err := ConvertRelativeTime("now") - after := time.Now().UTC() - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - - // Parse and verify it's within the expected time range - parsed, err := time.Parse(time.RFC3339, result) - s.NoError(err) - s.True(parsed.After(before.Add(-time.Second)) && parsed.Before(after.Add(time.Second)), - "Parsed time should be close to current time") - }) - - s.Run("handles RFC3339 timestamp unchanged", func() { - input := "2024-01-01T12:00:00Z" - result, err := ConvertRelativeTime(input) - - s.NoError(err) - s.Equal(input, result, "RFC3339 timestamp should be returned unchanged") - }) - - s.Run("handles Unix timestamp unchanged", func() { - input := "1704110400" - result, err := ConvertRelativeTime(input) - - s.NoError(err) - s.Equal(input, result, "Unix timestamp should be returned unchanged") - }) - - s.Run("handles relative time -10m", func() { - before := time.Now().UTC().Add(-10 * time.Minute) - result, err := ConvertRelativeTime("-10m") - after := time.Now().UTC().Add(-10 * time.Minute) - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - - parsed, err := time.Parse(time.RFC3339, result) - s.NoError(err) - s.True(parsed.After(before.Add(-time.Second)) && parsed.Before(after.Add(time.Second)), - "Parsed time should be approximately 10 minutes ago") - }) - - s.Run("handles relative time -1h", func() { - before := time.Now().UTC().Add(-1 * time.Hour) - result, err := ConvertRelativeTime("-1h") - after := time.Now().UTC().Add(-1 * time.Hour) - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - - parsed, err := time.Parse(time.RFC3339, result) - s.NoError(err) - s.True(parsed.After(before.Add(-time.Second)) && parsed.Before(after.Add(time.Second)), - "Parsed time should be approximately 1 hour ago") - }) - - s.Run("handles relative time -1d (days)", func() { - before := time.Now().UTC().Add(-24 * time.Hour) - result, err := ConvertRelativeTime("-1d") - after := time.Now().UTC().Add(-24 * time.Hour) - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - - parsed, err := time.Parse(time.RFC3339, result) - s.NoError(err) - s.True(parsed.After(before.Add(-time.Second)) && parsed.Before(after.Add(time.Second)), - "Parsed time should be approximately 1 day ago") - }) - - s.Run("handles relative time -30s (seconds)", func() { - before := time.Now().UTC().Add(-30 * time.Second) - result, err := ConvertRelativeTime("-30s") - after := time.Now().UTC().Add(-30 * time.Second) - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - - parsed, err := time.Parse(time.RFC3339, result) - s.NoError(err) - s.True(parsed.After(before.Add(-time.Second)) && parsed.Before(after.Add(time.Second)), - "Parsed time should be approximately 30 seconds ago") - }) - - s.Run("handles whitespace around input", func() { - result, err := ConvertRelativeTime(" now ") - - s.NoError(err) - s.Contains(result, "T", "Result should be RFC3339 format") - }) - - s.Run("returns error for invalid format", func() { - _, err := ConvertRelativeTime("invalid") - - s.Error(err) - s.Contains(err.Error(), "invalid time format") - }) - - s.Run("returns error for malformed relative time", func() { - _, err := ConvertRelativeTime("-abc") - - s.Error(err) - s.Contains(err.Error(), "invalid relative time format") - }) -} - func (s *PrometheusSuite) TestTruncateString() { s.Run("returns original string if shorter than max", func() { result := truncateString("hello", 10) @@ -436,32 +279,6 @@ func (s *PrometheusSuite) TestNewDefaultTLSConfig() { }) } -func (s *PrometheusSuite) TestParseIntFromString() { - s.Run("returns error for empty string", func() { - _, err := parseIntFromString("") - s.Error(err) - s.Contains(err.Error(), "empty string") - }) - - s.Run("returns error for number too large", func() { - _, err := parseIntFromString("12345678901") // 11 digits - s.Error(err) - s.Contains(err.Error(), "number too large") - }) - - s.Run("parses valid number", func() { - result, err := parseIntFromString("365") - s.NoError(err) - s.Equal(365, result) - }) - - s.Run("parses max allowed digits", func() { - result, err := parseIntFromString("1234567890") // exactly 10 digits - s.NoError(err) - s.Equal(1234567890, result) - }) -} - func TestPrometheusSuite(t *testing.T) { suite.Run(t, new(PrometheusSuite)) } diff --git a/pkg/prometheus/time.go b/pkg/prometheus/time.go deleted file mode 100644 index 49034d130..000000000 --- a/pkg/prometheus/time.go +++ /dev/null @@ -1,80 +0,0 @@ -package prometheus - -import ( - "fmt" - "strings" - "time" -) - -// ConvertRelativeTime converts relative time strings to RFC3339 timestamps. -// Supports: "now", "-10m", "-1h", "-1d", or passthrough for RFC3339/Unix timestamps. -func ConvertRelativeTime(timeStr string) (string, error) { - timeStr = strings.TrimSpace(timeStr) - - // If already a timestamp (contains T) or is numeric (Unix timestamp), return as-is - if strings.Contains(timeStr, "T") || isNumeric(timeStr) { - return timeStr, nil - } - - // Handle 'now' - if timeStr == "now" { - return time.Now().UTC().Format(time.RFC3339), nil - } - - // Handle relative times like '-10m', '-1h', '-1d', '-30s' - if strings.HasPrefix(timeStr, "-") { - // Parse duration (Go's time.ParseDuration doesn't support 'd' for days) - durationStr := timeStr[1:] // Remove leading '-' - - // Handle days specially - if strings.HasSuffix(durationStr, "d") { - days, err := parseIntFromString(strings.TrimSuffix(durationStr, "d")) - if err != nil { - return "", fmt.Errorf("invalid relative time format: %s", timeStr) - } - targetTime := time.Now().UTC().Add(-time.Duration(days) * 24 * time.Hour) - return targetTime.Format(time.RFC3339), nil - } - - // Parse standard durations (s, m, h) - duration, err := time.ParseDuration(durationStr) - if err != nil { - return "", fmt.Errorf("invalid relative time format: %s", timeStr) - } - targetTime := time.Now().UTC().Add(-duration) - return targetTime.Format(time.RFC3339), nil - } - - return "", fmt.Errorf("invalid time format: %s; expected 'now', relative time like '-10m', '-1h', '-1d', or RFC3339 timestamp", timeStr) -} - -// isNumeric checks if a string contains only digits. -func isNumeric(s string) bool { - if len(s) == 0 { - return false - } - for _, c := range s { - if c < '0' || c > '9' { - return false - } - } - return true -} - -// parseIntFromString parses an integer from a string with overflow protection. -func parseIntFromString(s string) (int, error) { - if len(s) == 0 { - return 0, fmt.Errorf("empty string") - } - if len(s) > 10 { // int32 max is 10 digits, prevents overflow - return 0, fmt.Errorf("number too large: %s", s) - } - var result int - for _, c := range s { - if c < '0' || c > '9' { - return 0, fmt.Errorf("invalid number: %s", s) - } - result = result*10 + int(c-'0') - } - return result, nil -} diff --git a/pkg/prometheus/types.go b/pkg/prometheus/types.go index da3fd2ce9..44f81e65c 100644 --- a/pkg/prometheus/types.go +++ b/pkg/prometheus/types.go @@ -27,28 +27,3 @@ type Result struct { // Values is used for range queries - [[timestamp, value], ...] Values [][]any `json:"values,omitempty"` } - -// Alert represents an Alertmanager alert. -type Alert struct { - Annotations map[string]string `json:"annotations"` - EndsAt string `json:"endsAt"` - Fingerprint string `json:"fingerprint"` - Receivers []Receiver `json:"receivers"` - StartsAt string `json:"startsAt"` - Status AlertStatus `json:"status"` - UpdatedAt string `json:"updatedAt"` - GeneratorURL string `json:"generatorURL,omitempty"` - Labels map[string]string `json:"labels"` -} - -// Receiver represents an Alertmanager receiver. -type Receiver struct { - Name string `json:"name"` -} - -// AlertStatus represents the status of an alert. -type AlertStatus struct { - InhibitedBy []string `json:"inhibitedBy"` - SilencedBy []string `json:"silencedBy"` - State string `json:"state"` -} diff --git a/pkg/toolsets/observability/alertmanager.go b/pkg/toolsets/observability/alertmanager.go deleted file mode 100644 index 47e68fc7b..000000000 --- a/pkg/toolsets/observability/alertmanager.go +++ /dev/null @@ -1,126 +0,0 @@ -package observability - -import ( - "fmt" - - "github.com/google/jsonschema-go/jsonschema" - "k8s.io/utils/ptr" - - "github.com/containers/kubernetes-mcp-server/pkg/api" -) - -// initAlertmanager returns the Alertmanager tools. -func initAlertmanager() []api.ServerTool { - return []api.ServerTool{ - initAlertmanagerAlerts(), - } -} - -// initAlertmanagerAlerts creates the alertmanager_alerts tool. -func initAlertmanagerAlerts() api.ServerTool { - return api.ServerTool{ - Tool: api.Tool{ - Name: "alertmanager_alerts", - Description: `Query active and pending alerts from the cluster's Alertmanager. -Useful for monitoring cluster health, detecting issues, and incident response. - -Returns alerts with their labels, annotations, status, and timing information. -Can filter by active/silenced/inhibited state. - -Common use cases: -- Check for critical alerts affecting the cluster -- Monitor for specific alert types (e.g., high CPU, disk pressure) -- Verify alert silences are working correctly`, - InputSchema: &jsonschema.Schema{ - Type: "object", - Properties: map[string]*jsonschema.Schema{ - "active": { - Type: "boolean", - Description: "Filter for active (firing) alerts. Default: true", - Default: api.ToRawMessage(true), - }, - "silenced": { - Type: "boolean", - Description: "Include silenced alerts in the results. Default: false", - Default: api.ToRawMessage(false), - }, - "inhibited": { - Type: "boolean", - Description: "Include inhibited alerts in the results. Default: false", - Default: api.ToRawMessage(false), - }, - "filter": { - Type: "string", - Description: "Optional filter using Alertmanager filter syntax. Examples: 'alertname=Watchdog', 'severity=critical', 'namespace=openshift-monitoring'", - }, - }, - }, - Annotations: api.ToolAnnotations{ - Title: "Alertmanager: Get Alerts", - ReadOnlyHint: ptr.To(true), - DestructiveHint: ptr.To(false), - IdempotentHint: ptr.To(true), - OpenWorldHint: ptr.To(true), - }, - }, - Handler: alertmanagerAlertsHandler, - } -} - -// alertmanagerAlertsHandler handles Alertmanager alerts queries. -func alertmanagerAlertsHandler(params api.ToolHandlerParams) (*api.ToolCallResult, error) { - // Validate endpoint (security check) - endpoint := "/api/v2/alerts" - if err := validateAlertmanagerEndpoint(endpoint); err != nil { - return api.NewToolCallResult("", err), nil - } - - // Get Alertmanager URL - baseURL, err := getRouteURL(params.Context, params, alertmanagerRoute, getMonitoringNamespace(params)) - if err != nil { - return api.NewToolCallResult("", fmt.Errorf("failed to get Alertmanager route: %w", err)), nil - } - - // Handle active parameter (default: true) - active := true - if v, ok := params.GetArguments()["active"].(bool); ok { - active = v - } - - // Handle silenced parameter (default: false) - silenced := false - if v, ok := params.GetArguments()["silenced"].(bool); ok { - silenced = v - } - - // Handle inhibited parameter (default: false) - inhibited := false - if v, ok := params.GetArguments()["inhibited"].(bool); ok { - inhibited = v - } - - // Handle optional filter - filter := "" - if f, ok := params.GetArguments()["filter"].(string); ok && f != "" { - // Validate filter length - if len(f) > maxQueryLength { - return api.NewToolCallResult("", fmt.Errorf("filter exceeds maximum length of %d characters", maxQueryLength)), nil - } - filter = f - } - - // Create client and execute request - client := newPrometheusClient(baseURL, params) - body, err := client.GetAlertsRaw(params.Context, active, silenced, inhibited, filter) - if err != nil { - return api.NewToolCallResult("", fmt.Errorf("alertmanager query failed: %w", err)), nil - } - - // Format response - result, err := prettyJSON(body) - if err != nil { - return api.NewToolCallResult(string(body), nil), nil - } - - return api.NewToolCallResult(result, nil), nil -} diff --git a/pkg/toolsets/observability/config.go b/pkg/toolsets/observability/config.go deleted file mode 100644 index 64653fdd1..000000000 --- a/pkg/toolsets/observability/config.go +++ /dev/null @@ -1,36 +0,0 @@ -package observability - -import ( - "context" - - "github.com/BurntSushi/toml" - "github.com/containers/kubernetes-mcp-server/pkg/api" - "github.com/containers/kubernetes-mcp-server/pkg/config" -) - -// Config holds observability toolset configuration -type Config struct { - // MonitoringNamespace is the namespace where monitoring components are deployed. - // Defaults to "openshift-monitoring" if not specified. - MonitoringNamespace string `toml:"monitoring_namespace,omitempty"` -} - -var _ api.ExtendedConfig = (*Config)(nil) - -// Validate checks that the configuration values are valid. -func (c *Config) Validate() error { - // All fields are optional with sensible defaults, no validation required - return nil -} - -func observabilityToolsetParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (api.ExtendedConfig, error) { - var cfg Config - if err := md.PrimitiveDecode(primitive, &cfg); err != nil { - return nil, err - } - return &cfg, nil -} - -func init() { - config.RegisterToolsetConfig("observability", observabilityToolsetParser) -} diff --git a/pkg/toolsets/observability/helpers.go b/pkg/toolsets/observability/helpers.go deleted file mode 100644 index f5420ac69..000000000 --- a/pkg/toolsets/observability/helpers.go +++ /dev/null @@ -1,168 +0,0 @@ -package observability - -import ( - "context" - "encoding/json" - "fmt" - "regexp" - "sync" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog/v2" - - "github.com/containers/kubernetes-mcp-server/pkg/api" - "github.com/containers/kubernetes-mcp-server/pkg/prometheus" -) - -const ( - // defaultMonitoringNamespace is the default namespace for OpenShift monitoring components - defaultMonitoringNamespace = "openshift-monitoring" - - // thanosQuerierRoute is the route name for Thanos Querier - thanosQuerierRoute = "thanos-querier" - - // alertmanagerRoute is the route name for Alertmanager - alertmanagerRoute = "alertmanager-main" - - // maxQueryLength is the maximum allowed query length to prevent DoS - maxQueryLength = 10000 -) - -// routeGVR is the GroupVersionResource for OpenShift Routes -var routeGVR = schema.GroupVersionResource{ - Group: "route.openshift.io", - Version: "v1", - Resource: "routes", -} - -// routeURLCache caches resolved route URLs for the lifetime of the server process. -// This avoids repeated Kubernetes API calls since routes rarely change. -// Key format: "apiServerHost/namespace/routeName", value: URL string. -// The API server host is included to support multi-cluster (ACM) environments. -var routeURLCache sync.Map - -// allowedPrometheusEndpoints is a whitelist of allowed Prometheus API endpoints -var allowedPrometheusEndpoints = map[string]bool{ - "/api/v1/query": true, - "/api/v1/query_range": true, - "/api/v1/series": true, - "/api/v1/labels": true, -} - -// allowedPrometheusLabelPattern matches /api/v1/label/