Plan pipeline operator enrollment

This commit is contained in:
2026-09-18 12:44:26 +00:00
parent eda361fa5d
commit a5bc701f60
4 changed files with 616 additions and 15 deletions

View File

@@ -0,0 +1,240 @@
# Feature 0014: Enroll Existing Pipeline Operators
Status: ready
## Summary
Let consumers name existing human accounts that the D&D pipeline role should
add to its shared runtime group. The interface is empty by default, manages
membership additively, and leaves account creation and removal outside the
role.
## Problem or Opportunity
The pipeline's shared filesystem and credential policy already supports
invocation by the dedicated `narratio` service account or by a human member of
the selected runtime group. The role does not yet select human operators or
manage their membership. Consumers must currently repeat a small
`ansible.builtin.user` task beside the role even though group enrollment is a
natural suite-level access choice.
The role must not silently become a general account-management interface.
Human account identity, login policy, credentials, home directories, and
removal remain host or organization concerns. The pipeline needs only a
concise way to enroll explicitly selected existing accounts in the group that
already defines pipeline authorization.
## Governing Policies
- [The Ansible house style](../../ansible.md) requires the smallest direct
desired-state implementation and permits state inspection only when its
result is independently required.
- [The testing house style](../../testing.md) requires a representative
public-interface test of deployed behavior and immediate clean convergence,
without retesting `getent` or `user` module internals.
- [The MVP roadmap](../mvp.md) defines the runtime group as the shared
operator and credential-authorization boundary.
- [Feature 0009](0009-shared-pipeline-filesystem-and-execution.md) establishes
the service-user and human-group-member execution model.
- [Feature 0011](0011-managed-pipeline-credential-delivery.md) establishes
that runtime-group membership authorizes access to the suite's shared
credentials.
- [Feature 0012](0012-collection-assembly.md) makes the standalone pipeline
repository authoritative and requires reviewed changes to enter the
collection through its public Gitea subtree source.
## Scope
- Add `dnd_pipeline_operator_users`, a list of existing account names that
defaults to an empty list.
- Declare the variable as a list of strings in the pipeline role's argument
specification.
- Require every selected account to exist before changing any selected
account's group membership.
- Add each selected account to `dnd_pipeline_runtime_group` with additive
supplementary-group semantics.
- Preserve the existing account, its primary group, and all other
supplementary groups.
- Leave omitted accounts and memberships unmanaged.
- Document the existing-account contract, additive behavior, authorization
consequence, and need to begin a new login session before newly granted
supplementary membership is visible to a long-lived user session.
- Adapt the existing pipeline integration fixture to exercise enrollment
through the new public variable and retain its representative configuration,
secret-access, shared-cache, and clean-repeat behavior.
- Import the reviewed, pushed pipeline commit into the collection through the
established squashed subtree workflow and validate the assembled artifact.
## Non-goals
- Creating, deleting, locking, or otherwise managing human accounts.
- Managing UIDs, primary groups, passwords, SSH keys, shells, home
directories, sudo access, or login policy.
- Removing a user from the runtime group when the user is omitted from the
list or removed from a later inventory value.
- Managing external identity-provider or directory-service membership.
- Adding per-user access controls, separate credential sets, or a second
operator group.
- Changing the `narratio` service account, runtime group, filesystem modes,
ACL policy, credential policy, or application configuration.
- Adding handlers, sidecar state, membership snapshots, post-change
verification, or a custom account-management abstraction.
- Publishing a new pipeline-role or collection release as part of this
implementation.
## Target End State
The pipeline role exposes this optional interface:
```yaml
dnd_pipeline_operator_users:
- eric
- alice
```
The default is `[]`, so existing consumers retain their current behavior. If
the list is nonempty, every named account must already exist on the managed
host. The role fails before its enrollment task when any selected account is
absent; it does not create a partially specified account as a side effect.
Each existing selected account is an additive member of
`dnd_pipeline_runtime_group`. Existing primary and supplementary memberships
remain intact. Membership in that group grants the pipeline access already
defined by Features 0009 and 0011, including shared writable runtime roots and
group-readable suite credentials. Existing login sessions may need to be
restarted before the operating system exposes newly granted supplementary
membership.
Removing a name from `dnd_pipeline_operator_users` does not revoke membership.
Account and access removal require an explicit lifecycle policy and remain
separate work.
The collection's `rakestraw.dnd.pipeline` role contains the same reviewed
interface through a squashed import from the authoritative public pipeline
repository. The published `v0.1.0` release remains unchanged until a later
release feature selects and validates a new version.
## Repository Ownership
- `ansible-role-dnd-pipeline` owns the public variable, existing-account
prerequisite, additive enrollment, operator documentation, and detailed
behavioral integration coverage.
- `ansible-collection-dnd` owns the faithful subtree import and its existing
assembled-artifact validation boundary. It does not independently
reimplement or duplicate the pipeline behavior test.
- `ansible-workspace-dnd` owns this roadmap, the settled MVP decision, active
implementation plan, status, and review record.
- The standalone application roles, application sources, asset repositories,
and `rakestraw-ansible` remain unchanged.
## Constraints and Settled Decisions
- The public interface is a simple list of usernames, not a mapping of account
definitions. A richer structure would imply ownership of account creation
and login policy that this feature expressly excludes.
- All selected accounts must exist before enrollment begins. A single
`ansible.builtin.getent` loop over the `passwd` database is justified because
`ansible.builtin.user` with `state: present` would otherwise create a missing
account. No registered fact, separate assertion, or post-enrollment query is
required.
- The enrollment task uses `ansible.builtin.user` with the selected runtime
group and `append: true`. The module owns membership convergence; the role
does not inspect or reconstruct current group lists.
- The enrollment task uses `become: true`. The account-existence lookup does
not require elevation.
- The runtime group continues to be created by the existing Narratio role
dependency before the pipeline role's ordinary tasks execute.
- Empty input naturally skips both loops. No separate conditional, fact, or
branching task is needed.
- Omission is unmanaged, not absent. The feature adds access but does not
define revocation or exact-list reconciliation.
- No username grammar stricter than the managed host's account database is
introduced. The `getent` lookup and `user` module retain ownership of their
native input and error handling.
## Simplest Viable Implementation
Add the empty-list default and matching argument-spec entry. Add one
`ansible.builtin.getent` task that loops over the selected names with database
`passwd` and relies on its default missing-key failure, followed by one
privileged `ansible.builtin.user` task that loops over the same names, selects
`dnd_pipeline_runtime_group`, and uses `append: true`.
Replace the README's consumer-owned membership example with the role variable
and its exact ownership boundary. In the existing integration driver, create
the temporary operator without `narratio` membership before the first role
application, pass that account through `dnd_pipeline_operator_users`, and
retain the existing native configuration and shared-access exercises. Do not
add a missing-user test, inspect `/etc/group`, assert module return data, or
create a second operator scenario.
After the authoritative pipeline change is committed, validated, and pushed,
use the documented public Gitea `git subtree pull --squash` workflow to import
that exact commit into `ansible-collection-dnd`. Run the collection's existing
artifact-boundary integration suite without expanding its detailed feature
matrix.
## Rationale and Alternatives
Keeping membership entirely in each consumer play would avoid two role tasks,
but it leaves every deployment to reproduce access policy already owned by the
suite. An empty-by-default list adds the ergonomic path without affecting
consumers that prefer external membership management.
Passing complete account definitions to the pipeline role would permit it to
create users, but it would also require defaults and policy for homes, shells,
authentication, IDs, and account removal. Those concerns are not pipeline
requirements and belong to host identity management.
Calling `ansible.builtin.user` without first checking existence would be more
compact but would create missing accounts with implicit properties. The
`getent` lookup is therefore independently required to enforce the selected
ownership boundary; it is not defensive re-verification of state already
owned by another module.
Exact-list reconciliation would make removal declarative, but omission has
not been selected as revocation and could remove access managed elsewhere.
Additive enrollment is the smaller and safer contract until lifecycle policy
is designed explicitly.
## Acceptance Criteria
- `dnd_pipeline_operator_users` defaults to an empty list and is declared as a
list of strings in the public argument specification.
- An empty list introduces no account or membership change.
- Every explicitly selected account must already exist, and a missing account
causes convergence to fail before the enrollment task can create or modify
any selected account.
- Each selected existing account is added to
`dnd_pipeline_runtime_group` additively without replacing its other groups.
- Omitted accounts and prior memberships remain unmanaged.
- The role uses only one account-existence task and one enrollment task; it
adds no handler, registered fact, assertion layer, command, private state,
or post-change verification.
- The pipeline documentation explains the variable, existing-account
prerequisite, additive non-removal behavior, group authorization boundary,
and login-session caveat.
- The existing temporary-operator integration scenario enrolls its fixture
through the public variable, demonstrates representative pipeline access,
and the immediate identical second role application reports `changed=0`
and `failed=0`.
- Syntax checking, production-profile lint, the complete standalone pipeline
integration suite, and `git diff --check` pass.
- The exact reviewed pipeline commit is pushed to public Gitea before the
collection imports it.
- The collection subtree contains the reviewed pipeline change through the
documented remote-only squashed import, and the existing collection
integration suite passes against the built and installed artifact.
- No application repository, asset repository, release tag, published
artifact, application configuration, filesystem policy, or credential value
changes.
## Dependencies and Follow-up Work
Features 0009, 0011, 0012, and 0013 are prerequisites. The completed Feature
0013 workspace and collection closeout commits should be pushed before the
implementation sprint begins so local and remote baselines agree.
Upgrade, downgrade, account revocation, exact-membership reconciliation,
application removal, and publication of a release containing this feature
remain separate future work.