Documentation
¶
Overview ¶
Package volumes translates a named, tenant-scoped platform volume into a concrete mount the existing pkg/mounts pipeline already knows how to realize.
A platform volume is NOT a new storage primitive. The operator configures one shared backend (S3 or NFS); a volume named "data" for tenant T becomes a synthesized models.MountSpec pointing at a deterministic, per-tenant path:
s3 → s3://<bucket>/<prefix>/<tenant>/<name>/ (operator creds) nfs → <server>:/<export>/<tenant>/<name> (no creds, kernel mount)
Because the path is keyed by an operator-derived tenant segment plus a sanitized name, the *backing store* is the single source of truth: any node (cluster or not) that mounts the same coordinates sees the same data. That is what makes platform volumes correct under cluster placement, where a sandbox can land on a different host than the one that first created the volume.
This package is deliberately free of dependencies on internal/config and the auth layer so it stays pure and offline-testable. The daemon maps its config into Backend; the service extracts the caller identity into Caller.
Index ¶
- Constants
- func BuildMountSpec(b Backend, tenant, name, target string, readOnly bool) (models.MountSpec, error)
- func BuildMountSpecForSource(b Backend, source, target string, readOnly bool) (models.MountSpec, error)
- func CheckQuota(currentCount, maxPerTenant int) error
- func ExecRunner(ctx context.Context, extraEnv []string, name string, args ...string) error
- func MountSource(b Backend, tenant, sanitizedName string) (string, error)
- func SanitizeVolumeName(name string) (string, error)
- func TenantScope(c Caller) (string, error)
- type Backend
- type Caller
- type Reclaimer
- type Runner
Constants ¶
const ( BackendS3 = "s3" BackendNFS = "nfs" )
const MaxVolumeNameLen = 64
MaxVolumeNameLen bounds a volume name so it can never blow up a storage path.
Variables ¶
This section is empty.
Functions ¶
func BuildMountSpec ¶
func BuildMountSpec(b Backend, tenant, name, target string, readOnly bool) (models.MountSpec, error)
BuildMountSpec synthesizes the MountSpec for one volume reference. tenant must already be a TenantScope result; name is sanitized here. The returned spec is fed straight into the existing seal + MountAll path; full target-path policy (blocked dirs, dup targets, count cap) is enforced by MountSpec.Validate in the service, not duplicated here.
func BuildMountSpecForSource ¶
func BuildMountSpecForSource(b Backend, source, target string, readOnly bool) (models.MountSpec, error)
BuildMountSpecForSource synthesizes a MountSpec for an explicit, already-known backend coordinate. This is the path callers use for an existing volume whose source was frozen at creation (volumes.source): the data must mount exactly where it was created even if the operator later reconfigured the bucket/prefix or export, so the stored source — not a recompute from current config — is authoritative. BuildMountSpec is the create-time wrapper that derives source from (tenant, name) and delegates here.
func CheckQuota ¶
CheckQuota enforces the per-tenant volume-count cap at create/attach time. maxPerTenant <= 0 means unlimited. currentCount is the tenant's existing distinct volume count; creating one more must not exceed the cap.
func ExecRunner ¶
ExecRunner is the production Runner: it executes the command (with extraEnv appended to the process environment) and folds any stderr/stdout into the returned error for operator logs. Reclaim commands produce little output, so capturing combined output is cheap.
func MountSource ¶
MountSource computes the deterministic backend coordinate for a volume. name must already be sanitized. It is the single source of truth shared by BuildMountSpec (to mount) and the attacher check (to recognize an existing mount of a given volume), so the two can never drift.
func SanitizeVolumeName ¶
SanitizeVolumeName normalizes a user-supplied volume name to a single, safe path segment. It lowercases, permits only [a-z0-9._-], and rejects anything that could escape its path component (separators, traversal, over-length, or the reserved "." / ".." names). The returned name is guaranteed to round-trip to exactly one path segment.
func TenantScope ¶
TenantScope returns the path segment that isolates one tenant's volumes from another's. This is the load-bearing security boundary: two tenants using the name "data" MUST map to disjoint paths. It is derived entirely server-side and is stable across restarts.
Types ¶
type Backend ¶
type Backend struct {
Kind string // "s3" or "nfs"
// S3 backend.
S3Bucket string
S3Prefix string
S3Region string
S3Endpoint string
S3AccessKeyID string
S3SecretAccessKey string
// NFS backend.
NFSServer string
NFSExport string
NFSOptions string
}
Backend is the operator-configured storage that platform volumes live on. It mirrors the relevant fields of config.PlatformVolumesConfig without importing it (which would couple this pure package to the env loader).
type Caller ¶
type Caller struct {
OwnerRef string // stable per-tenant account key (managed build)
Operator bool // true for the operator PAT path (no per-tenant identity)
HasAccess bool // false for background/internal calls with no auth edge
PATToken string // operator token, hashed as the fallback scope segment
}
Caller carries the authenticated identity the tenant scope is derived from. The service builds this from controlplane.AccessFromContext; we take primitives so this package never imports the auth layer.
- Operator/no-access (the open-source PAT path) has no per-tenant identity, so we fall back to a stable hash of the operator token. That collapses to a single self-host tenant, which is correct for single-tenant deploys.
- A validated user token carries a stable OwnerRef = the tenant key.
type Reclaimer ¶
type Reclaimer struct {
// contains filtered or unexported fields
}
Reclaimer deletes the backing bytes of a deleted platform volume. It is the missing consumer of the pending_volume_deletions ledger: DeletePlatformVolume only removes the metadata row and records coordinates; this is what actually reclaims the S3 prefix / NFS directory.
The repo deliberately carries no AWS SDK — sandbox mounts are realized by the mount-s3 binary — so backend deletion shells out to the same CLI tooling the host already needs (the AWS CLI for S3, mount(8) + the kernel NFS client for NFS). Commands run through an injected Runner so the command construction is unit-testable offline; make test never executes a real delete.
func NewReclaimer ¶
NewReclaimer builds a Reclaimer for the operator's configured backend. region / endpoint live on the Backend so S3 deletes hit the same store the mounts do.
func (*Reclaimer) Reclaim ¶
Reclaim deletes the data at source for the given backend kind. It is idempotent: deleting an already-empty or already-gone prefix/dir is success, so a ledger row can be retried until the row is removed. backendKind is the volume's stored backend (authoritative over the operator's current default, so a volume created under s3 still reclaims from s3 after a reconfigure).
type Runner ¶
Runner executes one external command with extraEnv appended to its environment. The default wires exec.CommandContext; tests inject a recorder. extraEnv carries S3 static credentials so the AWS CLI deletes with the same keys the mount path uses rather than relying on the host's ambient chain.