diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d10fb7..b707a9e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.16.1] - 2026-09-02 +- Fix: the Instances tab of the performance view now shows every pod of a k8s scope. The instance list had a hard cap of 10 that nothing could override, so a scope with 20 pods showed only the first 10 and the table had no next page. A `limit` in the request is honored, the `LIMIT` env var on the agent stays as the operator override, and with neither every pod is returned + ## [1.16.0] - 2026-09-02 - k8s scopes now show live, step-by-step progress in the dashboard when a scope is created, updated or deleted, and for every deployment action (deploy, switch traffic, finalize, rollback, delete, diagnose, kill instance, restart pods, pause/resume autoscaling, set instance count): each step with its duration, and long waits (load balancer, DNS, instance health) saying what they are waiting for. Requires `NP_API_KEY` on the agent; without it everything works as before - Failed k8s deployments now explain why on the step that failed, and what to do about it: image pull errors with the registry's message, out-of-memory kills, crash exit codes with the application's last log lines, and failing health checks with the path and response detected diff --git a/k8s/instance/build_context b/k8s/instance/build_context index ce0b3c89..d786191c 100644 --- a/k8s/instance/build_context +++ b/k8s/instance/build_context @@ -6,4 +6,9 @@ export APPLICATION_ID=$(echo "$ARGUMENTS" | jq -r 'if (.application_id | type) = export SCOPE_ID=$(echo "$ARGUMENTS" | jq -r 'if (.scope_id | type) == "array" then .scope_id[0] else .scope_id end') export DEPLOYMENT_ID=$(echo "$ARGUMENTS" | jq -r 'if (.deployment_id | type) == "array" then .deployment_id[0] else .deployment_id end') -export LIMIT=${LIMIT:-10} +REQUEST_LIMIT=$(echo "$ARGUMENTS" | jq -r '(if (.limit | type) == "array" then .limit[0] else .limit end) // empty') +LIMIT="${REQUEST_LIMIT:-$LIMIT}" +if ! [[ "$LIMIT" =~ ^[0-9]+$ ]]; then + LIMIT="" +fi +export LIMIT diff --git a/k8s/instance/list b/k8s/instance/list index a11a8d07..d0e21635 100644 --- a/k8s/instance/list +++ b/k8s/instance/list @@ -14,8 +14,8 @@ fi PODS=$(kubectl get pods -n "$K8S_NAMESPACE" -l "$LABEL_SELECTOR" -o json) -echo "$PODS" | jq --argjson limit ${LIMIT:-10} '{ - results: .items[:$limit] | map({ +echo "$PODS" | jq --argjson limit "${LIMIT:-0}" '{ + results: (if $limit > 0 then .items[:$limit] else .items end) | map({ id: .metadata.name, selector: .metadata.labels, details: { diff --git a/k8s/instance/tests/build_context.bats b/k8s/instance/tests/build_context.bats new file mode 100644 index 00000000..70bd76c6 --- /dev/null +++ b/k8s/instance/tests/build_context.bats @@ -0,0 +1,47 @@ +#!/usr/bin/env bats +# ============================================================================= +# Unit tests for instance/build_context - limit resolution +# ============================================================================= + +setup() { + export PROJECT_ROOT="$(cd "$BATS_TEST_DIRNAME/../../.." && pwd)" + unset LIMIT +} + +@test "no limit in the request and no LIMIT env leaves LIMIT empty" { + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ -z "$LIMIT" ] +} + +@test "limit in the request is exported as LIMIT" { + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9,"limit":25}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ "$LIMIT" = "25" ] +} + +@test "array-valued limit takes the first element" { + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9,"limit":["7"]}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ "$LIMIT" = "7" ] +} + +@test "LIMIT env is used when the request has no limit" { + export LIMIT=50 + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ "$LIMIT" = "50" ] +} + +@test "request limit wins over LIMIT env" { + export LIMIT=50 + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9,"limit":25}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ "$LIMIT" = "25" ] +} + +@test "non-numeric limit is ignored" { + export CONTEXT='{"arguments":{"application_id":1,"scope_id":9,"limit":"all"}}' + source "$PROJECT_ROOT/k8s/instance/build_context" + [ -z "$LIMIT" ] +} diff --git a/k8s/instance/tests/list.bats b/k8s/instance/tests/list.bats new file mode 100644 index 00000000..5013c6e1 --- /dev/null +++ b/k8s/instance/tests/list.bats @@ -0,0 +1,39 @@ +#!/usr/bin/env bats +# ============================================================================= +# Unit tests for instance/list - how LIMIT caps the pod list +# ============================================================================= + +setup() { + export PROJECT_ROOT="$(cd "$BATS_TEST_DIRNAME/../../.." && pwd)" + unset LIMIT APPLICATION_ID SCOPE_ID DEPLOYMENT_ID + + kubectl() { + jq -n '{items: [range(1; 4) | { + metadata: {name: "pod-\(.)", namespace: "nullplatform", creationTimestamp: "2025-01-01T00:00:00Z", labels: {}}, + status: {phase: "Running", podIP: "10.0.0.\(.)", containerStatuses: [{image: "app:x86"}]}, + spec: {nodeName: "node-1", containers: [{resources: {requests: {cpu: "100m", memory: "128Mi"}, limits: {cpu: "200m", memory: "256Mi"}}}]} + }]}' + } + export -f kubectl +} + +@test "without LIMIT every pod is returned" { + run bash "$PROJECT_ROOT/k8s/instance/list" + [ "$status" -eq 0 ] + [ "$(echo "$output" | jq '.results | length')" -eq 3 ] +} + +@test "LIMIT caps the list to the first N pods" { + export LIMIT=2 + run bash "$PROJECT_ROOT/k8s/instance/list" + [ "$status" -eq 0 ] + [ "$(echo "$output" | jq '.results | length')" -eq 2 ] + [ "$(echo "$output" | jq -r '.results[0].id')" = "pod-1" ] +} + +@test "LIMIT=0 means no cap" { + export LIMIT=0 + run bash "$PROJECT_ROOT/k8s/instance/list" + [ "$status" -eq 0 ] + [ "$(echo "$output" | jq '.results | length')" -eq 3 ] +}