Skip to content

feat(chaos): chaos_service CRUD, Service Discovery agent listing, and full load-test authoring parity (Locust/K6/JMeter) - #656

Merged
thisrohangupta merged 10 commits into
harness:mainfrom
priyanshuagarwal-harness:CHAOS-12430
Aug 14, 2026
Merged

feat(chaos): chaos_service CRUD, Service Discovery agent listing, and full load-test authoring parity (Locust/K6/JMeter)#656
thisrohangupta merged 10 commits into
harness:mainfrom
priyanshuagarwal-harness:CHAOS-12430

Conversation

@priyanshuagarwal-harness

@priyanshuagarwal-harness priyanshuagarwal-harness commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds MCP registry coverage for Chaos Service Management and the Service Discovery agent prerequisite flow, and brings chaos_loadtest create/update up to parity with the Harness UI's Load Test Studio across all three tools (Locust/K6/JMeter).

New resources

Resource Operations Purpose
chaos_service list, get, create, update, delete Onboard and manage logical chaos services (agent, environment, infra, probes)
discovered_agent list List Service Discovery agents; identity is used as agent_id when creating/updating a chaos service

chaos_k8s_infrastructure also gains a create operation (registration body).

chaos_service

  • List filters: environment_ids, infrastructure_ids, tags, include_all_scope, search, probe_ids, onboarding_id, sort/pagination.
  • Create/update map snake_case MCP fields to the v3 wire shape (externalServiceId, agentId, environmentId, infrastructureId, probe associations with inputs).
  • Execute actions: list_experiment_runs, list_load_tests (via serviceReferences).
  • Guided onboarding workflow documented in tool descriptions: discovered_agent -> (optional) discovered_namespace -> discovered_service -> (optional) chaos_probe -> harness_create(chaos_service).
  • New response extractor chaosServiceListExtract for the v3 list envelope { data, pagination }.

chaos_loadtest — schema migration + authoring parity

  • Wire shape migration: tunables and custom vars now live under toolConfig.<tool>.tunables / toolConfig.<tool>.variables (per LocustSpec/K6Spec/JMeterSpec in loadTestManager), replacing the old flat inputs[]. Added the update operation.
  • Custom Image mode parity across all three tools: script_image + script_entrypoint + load_args + image_pull_secret now build identical toolConfig.<tool>.script.{image,entrypoint,loadArgs,imagePullSecret} blocks for Locust, K6, and JMeter (JMeter's image branch previously lacked loadArgs/imagePullSecret).
  • JMeter-only additions: properties[] (JMeter -J/-G runtime property overrides) and thresholds[] (pass/fail criteria evaluated against .jtl run results: metric/stat/operator/value/abort_on_fail).
  • Shared Advanced Options (all tools): cleanup_policy (delete/retain) and resources (per-pod CPU/memory limits/requests, Kubernetes only) now flow through to both the wire body and the synthesized spec.yaml manifest, matching what the Load Test Studio UI writes.
  • Docs: descCreateLoadtest rewritten as a step-by-step guided flow (target type -> infra -> service -> tool type -> script/image mode -> load config -> Advanced Options) with per-tool YAML deltas, replacing stale/contradictory "JMeter has no scalar shortcuts" guidance.
  • Response extractor (extractors.ts) updated to read the new nested toolConfig.<tool>.{tunables,script,variables} shape instead of inputs[].

Tests & docs

  • New tests/registry/chaos-service.test.ts — list filter mapping, list_experiment_runs/list_load_tests execute actions.
  • tests/registry/chaos-loadtest.test.ts substantially expanded/refactored for the new toolConfig shape: Locust/K6/JMeter script + Custom Image coverage (incl. private-registry imagePullSecret, load_args validation), JMeter properties/thresholds/env_vars, and cleanupPolicy/resources pass-through + YAML emission.
  • README resource matrix updated (223 -> 225 resource types); chaos_k8s_infrastructure create and new execute actions documented.

Type of Change

  • New feature
  • Refactor (load-test schema alignment + authoring parity)
  • Documentation

Checklist

  • pnpm test passes
  • pnpm typecheck passes
  • pnpm build passes
  • pnpm standards:check passes
  • pnpm docs:check passes (resource count changed 223 -> 225; README updated)

Coding Standards (registry-driven MCP model)

  • No new server.registerTool() calls — only toolset definitions in src/registry/toolsets/.
  • chaos_service/discovered_agent registered under the existing chaos toolset.
  • operationPolicy set on every new/changed endpoint.
  • Shared response extractors from src/registry/extractors.ts (chaosServiceListExtract, reused sdPageExtract for agents).
  • identifierFields and scope declared on new resources.
  • No console.log() in src/.

Test plan

  • pnpm test tests/registry/chaos-service.test.ts
  • pnpm test tests/registry/chaos-loadtest.test.ts
  • Manual: harness_list(resource_type="discovered_agent") in a project with SD agents deployed.
  • Manual: harness_list(resource_type="chaos_service") with environment/infrastructure filters.
  • Manual: guided create flow — agent -> discovered service -> create chaos service with probes.
  • Manual: harness_create(resource_type="chaos_loadtest") for Locust/K6/JMeter in both script and Custom Image mode, incl. cleanup_policy="retain" and resources limits/requests.
  • Manual: harness_execute(resource_type="chaos_service", action="list_experiment_runs"/"list_load_tests", identity=...).

@priyanshuagarwal-harness
priyanshuagarwal-harness marked this pull request as ready for review July 31, 2026 07:44
@priyanshuagarwal-harness priyanshuagarwal-harness changed the title CHAOS: Adding MCP support for listing service agents & CURD operation… feat(chaos): add chaos_service CRUD and Service Discovery agent listing Aug 3, 2026
@priyanshuagarwal-harness priyanshuagarwal-harness changed the title feat(chaos): add chaos_service CRUD and Service Discovery agent listing feat(chaos): add chaos_service CRUD, service discovery agent listing, and update load test CRUD Aug 13, 2026
@priyanshuagarwal-harness priyanshuagarwal-harness changed the title feat(chaos): add chaos_service CRUD, service discovery agent listing, and update load test CRUD feat(chaos): chaos_service CRUD, Service Discovery agent listing, and full load-test authoring parity (Locust/K6/JMeter) Aug 13, 2026
Comment thread src/registry/extractors.ts Outdated
Comment thread src/registry/toolsets/chaos-descriptions.ts Outdated
Comment thread src/registry/toolsets/chaos.ts
Comment thread src/registry/toolsets/chaos.ts Outdated
Comment thread tests/registry/chaos-loadtest.test.ts
Comment thread src/registry/toolsets/chaos.ts
Comment thread src/registry/toolsets/chaos-descriptions.ts Outdated
Comment thread tests/registry/chaos-service.test.ts
Comment thread src/registry/toolsets/chaos-descriptions.ts Outdated
} else if (toolType === "Locust") {
toolBlock = buildLocustToolConfig(b, { scriptSource, script, targetUrl });
} else {
const supplied = b.tool_config as Record<string, unknown> | undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important (schema vs create): bodySchema advertises tool_config as a general escape hatch for Locust/K6/JMeter, but create only honors tool_config on the JMeter branch (Locust/K6 silently ignore it). Agents following the schema can believe they authored nested config when only scalars were used.

Either reject tool_config for Locust/K6 on create, or honor it like update’s escape hatch — and tighten the field description accordingly.

@cursor

cursor Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Architecture review (Sunil standards checklist) — request changes

Checked against docs/coding-standards.md + recurring Sunil architecture-review patterns (tasks/lessons.md Public Tool-Contract Discipline):

Passes

  • Registry-first only — no new server.registerTool() / tool handlers
  • Resources stay under existing chaos toolset; shared extractors in extractors.ts
  • scope / identifierFields / operationPolicy declared on new/changed endpoints
  • No console.log() / no toolset HarnessClient imports
  • CI green (pnpm build/test + smoke)
  • README resource count updated (223 → 225)

Fails / gaps (inline comments filed)

  1. Published field ≠ sourcerps_limit extractor vs options.rpsLimit (+ false test fixture)
  2. Docs/schema ≠ runtime contractenvironment_id “Required…” on optional agent list; JMeter taxonomy; entrypoint *; tool_config advertised for all tools but create-only for JMeter
  3. Silent dropsprobes: [], falsy abort_on_fail/send_to_engines, legacy inputs[] omitted without error
  4. Compact strips agent-required fieldsserviceCount on discovered_agent
  5. No focused create/update request-shape coverage for chaos_service / discovered_agent (recurring “no focused coverage” finding)
  6. Validation gap — Locust Custom Image without target_type=kubernetes

Nits (not blocking alone): execute size→limit alias parity; chaos_service deep-link; description substring vs regex wording.

Please address the blocking/important inlines before merge. Happy to re-review after.

(toolBlock as Record<string, unknown>).variables = b.variables;
}

const body: Record<string, unknown> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architecture note (migration): create builds a fresh toolConfig body and never accepts legacy flat inputs[] / scriptContent. Intentional, but old callers get silent omission of tunables rather than a loud error (Sunil: fail loudly / don’t silent-drop). If any legacy key is present, throw with a migration hint to the new scalars/toolConfig shape.

Comment thread src/registry/toolsets/chaos.ts Outdated
}
const out: JMeterThreshold = { metric, operator, value: t.value as number };
if (stat) out.stat = stat;
if (t.abort_on_fail === true) out.abortOnFail = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit / Sunil falsy-drop pattern: abort_on_fail: false and send_to_engines: false are omitted rather than sent as false (=== true gates). Examples/docs often show explicit false. Prefer != null and always project the boolean when provided, matching the “body builders must not silent-drop falsy values” architecture lesson.

(toolBlock as Record<string, unknown>).variables = b.variables;
}

const body: Record<string, unknown> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional follow-up (Sunil fail-loudly): legacy flat inputs[] / scriptContent are still silently ignored on create (tests assert they stay undefined). A loud migration error when those keys are present would save agents from “tunables vanished” debugging. Non-blocking if you’re intentionally hard-cutting.

Comment thread src/registry/toolsets/chaos.ts Outdated
}
const out: JMeterThreshold = { metric, operator, value: t.value as number };
if (stat) out.stat = stat;
if (t.abort_on_fail === true) out.abortOnFail = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (still open / Sunil falsy-drop): abort_on_fail: false and send_to_engines: false are still omitted via === true gates. Prefer projecting the boolean whenever the caller provided it (!= null) so explicit false survives round-trips.

Comment thread src/registry/toolsets/chaos.ts Outdated
{ name: "properties", type: "array", required: false, description: descLoadtestProperties },
{ name: "thresholds", type: "array", required: false, description: descLoadtestThresholds },
{ name: "variables", type: "array", required: false, description: "Custom template.Variable entries stored under toolConfig.<tool>.variables." },
{ name: "tool_config", type: "object", required: false, description: "Pass-through toolConfig object -- escape hatch for advanced/back-compat use (e.g. JMeter .zip bundles). Not required for Locust/K6/JMeter; prefer the scalar fields (script/script_image/properties/env_vars/thresholds/worker_count)." },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open (docs/schema ≠ create): create prose now says JMeter tool_config is the advanced escape hatch, but this bodySchema description still reads as a general Locust/K6/JMeter pass-through (“Not required for Locust/K6/JMeter”). Create still only honors tool_config on the JMeter branch — Locust/K6 silently ignore it.

Please tighten this field description to “JMeter-only on create” (or reject tool_config for Locust/K6) so agents don’t think nested authoring worked.

export const descCreateChaosService = `Onboard (create) a chaos service. This is a GUIDED, ORDERED workflow — do NOT skip ahead or invent identifiers; each step depends on the selection made in the previous one. Do NOT advance to the next step until the current step's selection is made, and NEVER call harness_create(chaos_service) until every step below is resolved and the user has confirmed.

STEP 1 — Select a Discovery Agent (REQUIRED FIRST; gate: do not continue without a chosen agent).
Call harness_list(resource_type='discovered_agent', org_id, project_id). Show each agent's name, identity, serviceCount, and last-discovery status. Ask the user to pick one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (still open): STEP 1 still asks agents to show “last-discovery status”, but compactDiscoveredAgent only keeps identity/name/.../serviceCount/networkMapCount/installationType/createdAt/updatedAt. If there is a real discovery-status field on the SD agent payload, keep it here; otherwise drop “last-discovery status” from STEP 1 so agents don’t invent one.

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Re-review (head 7eb25a73 — “updated based on review”)

Thanks for the follow-up. Focused Vitest: 53/53 passed (chaos-service, chaos-service-write, chaos-loadtest). CI green.

Addressed (resolved prior threads)

  • rps_limit: create now writes tunables.rpsLimit (aligned with extractor + tests); options.rpsLimit intentionally not authored
  • Locust Custom Image on Linux: intentional per loadTestManager — noted in code; no k8s-only guard
  • discovered_agent.environment_id: new descSDAgentListEnvironmentId (optional)
  • serviceCount compact: compactDiscoveredAgent added
  • Update probes: []: desired-state pass-through + required field + write tests
  • JMeter taxonomy / entrypoint docs: aligned with builders
  • Coverage: new chaos-service-write.test.ts (create/update/discovered_agent)
  • Nits: execute size→limit, chaos_service deep-link

Remaining (new inlines)

  1. Important: create tool_config bodySchema still implies Locust/K6/JMeter pass-through; create only honors JMeter
  2. Nit: STEP 1 “last-discovery status” vs compact whitelist (no status field kept)
  3. Nit: falsy abort_on_fail / send_to_engines still dropped
  4. Optional: loud error on legacy inputs[] / scriptContent

Verdict

Approve with nits — no blockers left for the Sunil architecture checklist. Prefer fixing (1) before merge; (2)–(4) can be follow-ups.

@thisrohangupta
thisrohangupta merged commit 9a55143 into harness:main Aug 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants