A project's `share` (absolute or a percentage of its parent's) is carved from the parent; siblings never add up to more than the parent. v1 is strict: a project holds at most its share. A project's own jobs and its children without a share draw from its unassigned part. Borrowing idle shares above a project's own (`max`) and reclaim are future work, in the renamed project-borrowing brief. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
8 KiB
Brief: project-config
(/kiro-discovery, 2026-10-09, Path E "configuration layering". Reference:
IBM Spectrum LSF 10.1 Administering LSF pp. 107 (default project), 446-453
(resource allocation limits, PROJECTS / PER_PROJECT); Using LSF on UNIX p21
(bsub -P).)
Problem
Projects are meant to control resource ownership: who may charge work to a project and how much of the cluster a project may use. Today a project is only a free-text label the user types; anyone can name any project, project limits cannot be configured end to end, and nothing shows usage per project.
Current State
- Submit data carries
project_name(JobSubmitData,controller.capnp:582→job_submit_data_t,types.h:356-368);xcecli jobs submit -P(xcecli/cmd/jobs/submit.go:60); the apiserver passes it through (backend/convert.go:307-317) without any check. - The scheduler maps an empty name to
"default"(sched_get_project,scheduler/src/core.c:617-623).SchedProject(capnp:1260) has name and priority but no config source. - Limits already accept project scopes (
LimitConfig,limits.yaml;CONSUMER_PROJECT,lib/xcelimit.c:23-26, vres key/p=<project>), but the scheduler's project-group managerpgmgris created empty (core.c:185) and never loaded (scheduler.c:1144-1145loads onlyugmgr/hgmgr), so project limits cannot be configured to work. - No project fairshare, accounting view or access check exists.
Desired Outcome (v1 = owned shares; user decisions 2026-10-09, 2026-10-12)
- Project definitions in the cluster configuration: path name,
description, members (users and user groups),
shareandmember_quota; plus a cluster default project. (No priority: queues own ordering.) Schema, YAML, JSON schema,xceadminmapping, live-upgrade replication. - Project selection at submit:
-P> the user's default project (if defined) > the cluster default >"default"(LSF p107 order, without environment variables). - Access check at submit (user decisions 2026-10-12): an RBAC
permission
project:submit, implied by membership of the project and grantable by roles (checked in the apiserver only: production submits always go through it, 2026-10-12), with a clear error;cluster_adminkeeps its existing bypass. - Project admins: a project's
adminsget every job permission andproject:viewover its subtree, implied by config like membership. Only the cluster admin edits projects in this stage (config push). - Owned shares (user decisions 2026-10-12, replacing "
PROJECTS/PER_PROJECTlimits inlimits.yaml"): written on the project itself, rolling up the tree, with pend reasons naming the project; seejob-config-layering.md"Resource control". - Visibility: project shown in job details and listings; per-project
usage against its limits (REST,
xcecli, REPLinfo limit). - Merge input: the project is one level for
job-config-merge(default project; any project-level defaults decided in requirements).
Approach
Follow the queue/usergroup config pattern (config-design.md): projects are
config entities pushed to controller and scheduler (Conf4Sched), the
scheduler fills pgmgr from them, and the existing limit machinery does the
rest. Projects already reach the scheduler on each job, so the WAL needs no
new job field.
Owned shares compile to limits (user decisions 2026-10-12). The
project tree is the only place concurrent use is controlled: a project owns
a share of its parent's resources and, in v1, holds at most that share (no
borrowing; project-borrowing). The limit plugin stays as the internal
mechanism and admins no longer write limits. Semantics:
job-config-layering.md "Shares".
projects:
- path: eng
members: [eng-ug]
share: { slots: 400, gpu: 16, mem: 2T } # carved from the cluster
member_quota: { slots: 64 } # per user, over eng's whole subtree
admins: [eng-leads] # job control over eng's subtree
- path: eng/hpc
members: [alice, bob]
share: { slots: 60% } # 60% of eng's 400 slots
- path: eng/ai
members: [carol]
share: { slots: 40%, gpu: 16 }
- path: eng/tools # no share: draws from eng's unassigned part
members: [eng-ug]
- On each config push, and when the cluster's capacity changes, the
scheduler generates per project P and resource r:
- a share limit
proj:<P>:<r>(project consumer {P} matching P and its descendants) with P's effective share. A nested percentage is the product of the percentages down from the root (an absolute amount once an ancestor is absolute), so it compiles to the plugin's existingpercentform, which since bug-1834 is a fraction of the static total capacity (limit.cupdate_limit_res_value); - an unassigned-part limit
projpool:<P>:<r>(consumer {P and its descendants}, excluding the subtrees of P's children with a share of r, using the consumer's existing exclude list) with P's share minus its children's shares; the root gets one when any top-level share of r exists; - for
member_quota,projm:<P>:<r>(same consumer as the share limit plus usersallper user, vres keylimit=projm:...:<r>/u=<user>,lib/xcelimit.c:402). New in the plugin: descendant matching on the project consumer.
- a share limit
- The path contains
/, which is also the vres key's field separator; the trace splitter (sched_trace_capture.c:40,sti_copy_scope) would renderp=eng/hpcasp=eng,hpc. Escape the project field in the key or use another internal separator (design). - Project-scoped entries in
limits.yamlare rejected byxceadminwith a pointer to project shares; the rest oflimits.yamlis retired bylimits-to-owners.
Scope
- In: project config model and pipeline (
share,member_quota); default project resolution; submit access check;pgmgrloading; shares compiled to limits and enforced end to end; display; tests (unit, sim, live). - Out: borrowing idle shares (
max), protecting owners from borrowers and reclaim by preemption (project-borrowing); fairshare; accounting history/billing.
Boundary Candidates
- Config model and pipeline.
- Submit resolution and access check (apiserver, controller).
- Scheduler project groups and limits.
- Display surfaces.
Out of Boundary
- Scheduling-order policy (queues), application settings (profiles).
Upstream / Downstream
- Upstream: config pipeline (
admin-config-tool,config-upgrade-snapshot),peer-authentication-hardening(authenticated apiserver connections), limit plugin (plugins/limit.c,lib/xcelimit.c), RBAC (rbac_check.go). - Downstream:
job-config-merge(project level),project-borrowing,limits-to-owners,lsf-config-migration,rbac-persistence(if access uses RBAC scopes).
Existing Spec Touchpoints
- Extends: none.
- Adjacent:
scheduler-sim-diagnostics(info limit),resource-distribution-mapping,rbac-persistence.
Design questions (RBAC, 2026-10-12)
- Resolved 2026-10-12 (user): production submits always go through the
apiserver; harness and direct client-library submits are test tools. The
apiserver resolves the project and checks
project:submit(membership and role grants); the controller records the project and authorization basis it is given and never re-checks RBAC. A direct submit is recorded as given with basisdirectin test mode (Req 3.13); in production modepeer-authentication-hardeningrejects it. - The job's authorization basis (Req 3.11) is a new job field: WAL fact and
scheduler snapshot must carry it (field completeness,
persistence.md).
Constraints
- Middleware chain unchanged; additive Cap'n Proto; old jobs keep their recorded project name even if the project is later removed from config (requirements decide what a pending job with a removed project does).
- Project paths are identities: a job keeps the path it was submitted under; usage of a running job is never moved by a tree change.