Skip to content

H7: Add role:* capability slots — catch missing IIS/Docker/nginx at plan-time

Summary

H7 of the 1.8.0 Upgrade Hardening initiative (H1-H6 already merged).

Closes the gap from the operator's actual production scenario where IIS deploy dispatched to a Windows machine without IIS installed, ran the script all the way to Import-Module WebAdministration, and ONLY THEN failed with "module not found". H7 lets handlers declare role requirements that CapabilityValidator catches at plan-time — before any wasted dispatch.

What's new in the taxonomy

public static class CapabilityKeys.Role  // H7
{
    public const string IIS     = "role:iis";      // Windows W3SVC
    public const string Docker  = "role:docker";   // Docker daemon
    public const string Nginx   = "role:nginx";    // nginx service (Linux)
    public const string Systemd = "role:systemd";  // systemd init (Linux)
}

IISDeployActionHandler.StaticRequirements now declares os:windows + shell:powershell + role:iis.

Agent-side detection

RuntimeCapabilitiesInspector.DetectInstalledRoles() probes cheaply (~50ms per role) and reports comma-separated in the new installedRoles metadata key:

Probe Tool
iis (Windows) sc.exe query W3SVC exit 0
docker (Windows) docker on PATH + com.docker.service registered
docker (Linux) docker on PATH + systemctl status docker.service exit 0/3
nginx (Linux) systemctl status nginx.service exit 0/3
systemd (Linux) systemctl on PATH

Probe failures are silent — operator just doesn't see that role advertised; handler requirements fall to optimistic-allow.

Wire path

Agent: RuntimeCapabilitiesInspector → metadata["installedRoles"] = "iis,docker"
   ↓ (Halibut CapabilitiesResponse)
Server: TentacleHealthCheckStrategy.CacheCapabilitiesFor
   ↓
   InMemoryMachineRuntimeCapabilitiesCache.InstalledRoles
   ↓ (H2 — also persisted to machine.runtime_capabilities_json)
   ↓
DeploymentPlanner → MachineCapabilitySet.From(caps)
                       ↓
                       projects "iis" into role:iis slot with value Present
   ↓
CapabilityValidator → IISDeployActionHandler.StaticRequirements
                         requires role:iis
                       MATCH SUCCESS or
                       FAIL with "Missing capability: role:iis" + hint

Backward compatibility (no big-bang migration)

  • Pre-H7 agents don't emit installedRoles metadata → cache has empty InstalledRoles → ProjectRoles produces no role:* slots → CapabilityValidator's "absent slot = unknown = optimistic-allow" path → existing fleets continue to plan/dispatch IIS deploy normally.
  • Strict validation activates only after the operator upgrades the agent to a version that detects + advertises roles. Per-machine, gradual.
  • H2 persistence DTO has InstalledRoles as nullable — blobs written by pre-H7 servers deserialise cleanly with null → string.Empty coalesce.

Test plan

  • +4 projection tests (Theory × 4 single-role + 1 multi-role + 1 empty-roles backward-compat + 1 whitespace/dupe normalisation)
  • DeploymentPlannerStaticRequirementsTests (PR #347/#348) already exercises the validator loop; H7's role:iis requirement plugs in without test changes
  • 5585/5585 unit tests green (+7 net new)
  • Integration: agent role probe behaviour deferred to H8 (E2E matrix includes "deploy IIS to machine without IIS installed → plan-time error" scenario)

Operator UX after H7

When operator tries to deploy IIS to a machine that lacks IIS:

Release validation failed for step 'Deploy to IIS':
  Machine 'WEB-01' is missing required capability: role:iis.
  Install IIS via Server Manager → Add Roles and Features → Web Server (IIS),
  then run an active health check (POST /api/machines/{id}/health-check)
  to repopulate the capability cache. Retry the release once role:iis appears
  in the machine's capability snapshot.

(Exact message format depends on CapabilityValidator.ValidateStaticRequirements's existing error renderer.)

What's NOT in this PR

  • H8: Comprehensive E2E test matrix (final — exercises the full upgrade + deploy chain end-to-end)

Merge request reports

Loading