teamhost

package
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

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

View Source
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

func WriteEntry(l Layout, entry *model.TeamEntry) error

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 NewLayout

func NewLayout(base string) Layout

NewLayout returns a Layout rooted at base.

func (Layout) CacheDir

func (l Layout) CacheDir() string

CacheDir is the materialized-source cache root (<base>/cache).

func (Layout) DropInDir

func (l Layout) DropInDir() string

DropInDir is the per-project drop-in directory (<base>/kuketeam.d).

func (Layout) EntryPath

func (l Layout) EntryPath(project string) string

EntryPath is the per-project file (<base>/kuketeam.d/<project>.yaml).

func (Layout) GlobalConfigPath

func (l Layout) GlobalConfigPath() string

GlobalConfigPath is the operator-global facts file (<base>/kuketeams.yaml).

func (Layout) HarnessSeedPath

func (l Layout) HarnessSeedPath(team, harness, variant string) string

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

func (l Layout) RoleHarnessStateDir(team, role, harness string) string

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

func (l Layout) SharedSecretsEnvPath() string

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

func (l Layout) TeamDir(team string) string

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

func (l Layout) TeamSecretsEnvPath(team string) string

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).

func (Layout) TeamsRoot

func (l Layout) TeamsRoot() string

TeamsRoot is the shared root under Base that holds per-team host-state subtrees and the host-wide secrets.env default (<base>/teams). The directory is operator-only (mode 0o700) when materialized.

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).

type SeedPair

type SeedPair struct {
	Role    string
	Harness string
}

SeedPair names one roster (role × harness) pair the provisioning pass materializes a state dir + seed set for.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL