Human-Run Resource Provider - 2026-10-30¶
Index · Shared Resource Pools · Resource Lifecycle Hooks - 2026-10-26
Context¶
Resource Lifecycle Hooks left a "provider" (create, reset, check, destroy, list) and dynamic pools as options. The owner's rule for them: QASight must never create or delete VMs on its own. Pools stay fixed lists of keys in qasight.yaml; the only automatic commands are the user's own hooks, which reset or check a key that already exists.
What a provider still adds under that rule: seeing whether a pool matches the real VMs (keys that vanished, leftovers outside the pool), and later a safe, logged way for a person to add or remove VMs.
Decision¶
A pool's provider: holds the user's own shell commands. QASight ships no VMware, cloud or hypervisor code and holds no credentials. Provider commands run only from qasight pool, typed by a person, on that person's machine.
- Read-only status (pull request 206).
provider.listprints the keys that exist.qasight pool status [POOL] [--check]compares that with the pool, shows each key's lease state, and with--checkruns the pool's existingon_checkon free keys (a held key is never checked). It changes nothing: no disable, no lease, no command besideslistandon_check. Exit1when a pool needs a look, so a CI schedule can warn. - Never automatic, enforced.
ResourcePool.to_jsonnever includesprovider, so it never reaches a team server or a stage's workers.tests/unit/qasight/test_pool_provider.pyfails if any module besidesqasight/cli/commands/pool.pyimportsqasight.resources.provider. on_checkis reused, not a secondprovider.check: one "is this key usable?" command, whether run on take or byqasight pool status --check.- Human-run changes.
qasight pool add / remove / sweeprun the user'sprovider.createandprovider.destroy:- a dry run unless
--apply, which asks first unless--yes; - refused unless the verb is under
provider.allow(which then needslist,name_prefix, and forcreatemax_items), and whenTESTO_PROVIDER_ACTIONS=0; - at most 5 keys per command;
addstops atmax_items(poolitemsplus whatlistshows); destroyonly on names withname_prefixthatlistshows, that nobody holds and that aren't initems;- nothing runs if
listfails.
- a dry run unless
- Every command is logged. Table
resource_action(pool, key, verb, user, host, command, ok, detail, time) in the lease database, or on the team server throughPOST /api/v1/resources/actions(a write: token orrunner; the account name wins over the client's).GET /api/v1/resources/actions,qasight pool logand a "Provider commands" card on the Resources page show it. If logging fails, the command stops before running anything else. - The config stays the source of truth.
addnever editsqasight.yaml; the person adds new keys toitems, and takes a key out ofitemsbefore removing it.
Options considered¶
- Stop at fixed pools (no provider). Safe, but drift and leftovers stay invisible until a test fails on a missing VM.
- Automatic dynamic pools (create when the pool runs dry, destroy when a lease ends). This is how Testcontainers and cloud autoscalers work. Rejected: it breaks the owner's rule even if off by default, and brings quotas, cost and orphan handling.
- A Python provider class (
provider: module:Class). Possible later under the same rules; shell commands cover the users today.
Consequences¶
- Nothing changes for a pool without
provider:.qasight pool statuson such a pool shows lease states and?for existence. - A failing
provider.listis reported (exit3), never guessed from: existence shows?. - Leftovers are only reported by
status. Removing them is a person's job: their own scripts, orqasight pool sweep --apply. - Adding a VM is two steps (
qasight pool add --apply, then add it toitems). That is on purpose: no key enters or leaves a pool without a person.