Documentation
¶
Overview ¶
Package teamhost owns the host-side, on-disk lifecycle of the team-distribution config maintained by `kuke team init` (epic #792, step 1 #796): the operator-global facts file (~/.kuke/kuketeams.yaml) and the per-project drop-in directory (~/.kuke/kuketeam.d/<project>.yaml).
The drop-in layout (the systemd/sudoers.d pattern) replaces a single shared teams[] array: each project owns one file, so a corrupt or partial write — which can happen on every init — touches one project rather than the whole roster, and two concurrent `kuke team init` runs never race on a shared array. The Base directory is injected (rather than resolved from $HOME here) so the lifecycle is unit-testable against a temp dir with no live kukeond.
Index ¶
- Constants
- func EnsureGlobalConfig(l Layout, cfg *model.TeamsConfig) (bool, error)
- func ProvisionTeam(in ProvisionInputs) error
- func WriteEntry(l Layout, entry *model.TeamEntry) error
- type Layout
- func (l Layout) CacheDir() string
- func (l Layout) DropInDir() string
- func (l Layout) EntryPath(project string) string
- func (l Layout) GlobalConfigPath() string
- func (l Layout) HarnessSeedPath(team, harness, variant string) string
- func (l Layout) RoleHarnessStateDir(team, role, harness string) string
- func (l Layout) SharedSecretsEnvPath() string
- func (l Layout) TeamDir(team string) string
- func (l Layout) TeamSecretsEnvPath(team string) string
- func (l Layout) TeamsRoot() string
- type ProvisionInputs
- type SeedPair
Constants ¶
const ( // TeamsRootPerm is the shared mode used for the teams/ root, each // TeamDir(team), and the per-(role × harness) state subdirs. TeamsRootPerm = 0o700 )
Variables ¶
This section is empty.
Functions ¶
func EnsureGlobalConfig ¶
func EnsureGlobalConfig(l Layout, cfg *model.TeamsConfig) (bool, error)
EnsureGlobalConfig writes cfg to the global facts path only when no file is already present, returning created=true when it scaffolded the file and false when one already existed (the re-run case — its contents are left untouched). The parent directory is created (0o700) if absent.
func ProvisionTeam ¶
func ProvisionTeam(in ProvisionInputs) error
ProvisionTeam runs the per-team provisioning pass: it mkdir -p's each roster pair's state dir and writes any harness seeds that are not already present (hand-edited files survive). The pass is idempotent — a re-run on a healthy host is a no-op.
DryRun reports what would change to Out without touching disk; an empty Out is treated as io.Discard.
func WriteEntry ¶
WriteEntry persists entry to its per-project path, creating the drop-in directory (0o700) if absent. Only the named project's file is touched, so rewriting one project's entry never disturbs another's. The write is atomic (temp file + rename) and the resulting file is 0o600. The name is also re-checked for path-traversal characters as defense-in-depth — the parser already rejects unsafe names, but callers that build a TeamEntry directly (without going through the parser) must not be able to escape the drop-in directory.
Types ¶
type Layout ¶
type Layout struct {
// Base is the directory holding kuketeams.yaml and kuketeam.d/.
Base string
}
Layout resolves the team-distribution file paths under a base directory (normally ~/.kuke). The zero value is unusable; construct with NewLayout.
func (Layout) GlobalConfigPath ¶
GlobalConfigPath is the operator-global facts file (<base>/kuketeams.yaml).
func (Layout) HarnessSeedPath ¶
HarnessSeedPath is the canonical seed-file path for a harness under TeamDir(team): `<base>/teams/<team>/<harness>.json` when variant is empty, or `<base>/teams/<team>/<harness>.json-<variant>` when set (e.g. variant="root" → "<harness>.json-root"). The provisioning pass resolves a harness seed's spec.path template against this canonical shape — this method exists so callers (tests, future verbs) can address the path without re-deriving the layout.
func (Layout) RoleHarnessStateDir ¶
RoleHarnessStateDir is the per-(role × harness) state directory under TeamDir(team): `<base>/teams/<team>/<role>-<harness>/`. The provisioning pass mkdir -p's this for every roster pair.
func (Layout) SharedSecretsEnvPath ¶
SharedSecretsEnvPath is the host-wide secrets.env default (<base>/teams/secrets.env). It carries operator-global defaults shared across every team and is operator-only (mode 0o600).
func (Layout) TeamDir ¶
TeamDir is the per-team host-state root (<base>/teams/<team>). The operator may relocate this via TeamEntry.spec.teamDir; callers that need the override path should consult the persisted entry, not this method.
func (Layout) TeamSecretsEnvPath ¶
TeamSecretsEnvPath is the per-team secrets.env override (<base>/teams/<team>/secrets.env). Per-team values override the shared defaults and the file is operator-only (mode 0o600).
type ProvisionInputs ¶
type ProvisionInputs struct {
TeamRoot string
Pairs []SeedPair
Harnesses map[string]*model.Harness
DryRun bool
Out io.Writer
}
ProvisionInputs is the per-team provisioning request.
TeamRoot is the resolved per-team root directory — `<base>/teams/<team>` by default, or the operator-supplied TeamEntry.spec.teamDir override when set. The provisioning pass anchors every state dir and seed write under this path so a relocated TeamDir lands a fully self-contained tree.
Pairs lists the roster (role × harness) entries to mkdir state dirs for. Harnesses maps each harness referenced by Pairs to its loaded Harness document; ProvisionTeam reads `Spec.Seeds` off the corresponding entry to write seed files. A harness referenced by Pairs but absent from Harnesses gets a state dir but no seeds (the resolve layer would surface the missing-harness error earlier — this is a defense-in-depth path).