pub struct WorkloadSpec {Show 23 fields
pub schema_version: SchemaVersion,
pub name: String,
pub image: ImageRef,
pub tier: TierTag,
pub tenant: TenantId,
pub namespace: NamespaceId,
pub replicas: u32,
pub command: Option<Vec<String>>,
pub entrypoint: Option<Vec<String>>,
pub workdir: Option<PathBuf>,
pub user: Option<String>,
pub env: Vec<EnvVar>,
pub secrets: Vec<SecretMount>,
pub volumes: Vec<VolumeMount>,
pub resources: ResourceLimits,
pub depends_on: Vec<MeshIdent>,
pub healthcheck: Option<Healthcheck>,
pub restart_policy: RestartPolicy,
pub archetype: Option<LifecycleArchetype>,
pub stop_policy: StopPolicy,
pub expose: ExposeSpec,
pub labels: HashMap<String, String>,
pub annotations: HashMap<String, String>,
}Expand description
Complete typed description of a containerd workload handed to yubaba over
RPC. This is also the payload of the kind = "container" variant of
Workload on disk.
Yubaba never accepts compose YAML on its RPC surface — agents, the desktop,
and operator CLIs all hand yubaba WorkloadSpec values. See the arch doc
for the validation layers and evolution rules.
Fields§
§schema_version: SchemaVersionWire-format version; always V1 today. Present at the top level so
rolling clusters can detect and migrate across schema generations.
name: StringDNS-friendly workload name, e.g. "noisetable-api". Regex:
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$, length ≤ 63.
image: ImageRefContainer image to pull.
tier: TierTagTier tag controlling admission control and mesh filtering.
tenant: TenantIdTenant isolation axis (W206). Separates operators’ workloads at the
network / DB / mesh-identity level. Defaults to TenantId::singleton
for specs that predate the axis, so single-tenant clusters keep every
isolation primitive a no-op. Orthogonal to Self::tier (class) and
Self::namespace (routing).
namespace: NamespaceIdNamespace routing/naming axis (W206). A pure naming key — never
affects isolation; disambiguates DNS names and selects config root /
provider zone within a tenant. Defaults to NamespaceId::singleton.
replicas: u32Target replica count. 0 registers the workload without deploying it.
Range: 0–100 (cluster-wide cap; operator can raise it).
command: Option<Vec<String>>Override the image’s CMD. None leaves the image default.
entrypoint: Option<Vec<String>>Override the image’s ENTRYPOINT. None leaves the image default.
workdir: Option<PathBuf>Working directory inside the container.
user: Option<String>User to run as, e.g. "1000:1000" or "appuser".
env: Vec<EnvVar>Environment variables. Values may be literals, secret refs, or mesh-address references resolved by yubaba at deploy time.
secrets: Vec<SecretMount>Secret mounts. Values never appear in the spec JSON — only references.
volumes: Vec<VolumeMount>Volume mounts.
resources: ResourceLimitsHard resource caps enforced by containerd/cgroups.
depends_on: Vec<MeshIdent>Mesh idents that must reach Ready before this workload starts.
healthcheck: Option<Healthcheck>Container liveness/readiness probe.
restart_policy: RestartPolicyWhat yubaba does when the container exits.
archetype: Option<LifecycleArchetype>Explicit lifecycle archetype (R572-F1 / W244): server, appliance,
or job. None means the spec predates this field (or the author
didn’t set it) — callers MUST NOT read this directly to decide
drainability; use WorkloadSpec::effective_archetype, which falls
back to the pre-R572 volumes/restart_policy inference so no
existing spec’s effective meaning changes.
Additive: this field did not exist before R572-F1. Reconciler (F4) and scheduler (F5) branching on the resolved archetype are separate, later tickets — this field alone changes no runtime behavior.
stop_policy: StopPolicyGraceful shutdown configuration.
expose: ExposeSpecNetwork exposure configuration — mesh, public, and operator channels are independent and can be set in any combination.
labels: HashMap<String, String>OCI-style labels, passed through to the container. Opaque to yubaba.
annotations: HashMap<String, String>Yah-specific metadata, conventionally prefixed yah.*. Opaque to
yubaba beyond yah.forge=true which suppresses the Never-restart guard.
Implementations§
Source§impl WorkloadSpec
impl WorkloadSpec
Sourcepub fn for_forge(
forge_id: &str,
image: ImageRef,
tier: TierTag,
ports: Vec<u16>,
) -> Self
pub fn for_forge( forge_id: &str, image: ImageRef, tier: TierTag, ports: Vec<u16>, ) -> Self
Build a WorkloadSpec for a forge run.
Sets the conventional forge fields in one place so callers cannot forget any of them:
restart_policy = Neverarchetype = Some(LifecycleArchetype::Job)— a forge run is exactly thecontainer-kind instance of the job archetype (W244); set explicitly rather than left to infer since this constructor knows its own shapeexpose.public = None,expose.operator = Noneexpose.mesh.identity = "forge.<forge_id>"annotations["yah.forge"] = "true"(suppresses the shape warning)tierandimagecome from the caller;portsbecomes the mesh port list (empty is valid — forge jobs often don’t expose ports)
All other fields are set to safe defaults. Callers can mutate the
returned value to fill in command, env, resources, etc.
Sourcepub fn wants_host_network(&self) -> bool
pub fn wants_host_network(&self) -> bool
Whether this workload requests the host network namespace rather than an isolated one.
Opt-in via annotations["yah.network"] == "host" (see
HOST_NETWORK_ANNOTATION / HOST_NETWORK_VALUE). Default is the
isolated netns every other workload gets — host networking is a
privileged escape hatch for the few infra workloads that must bind a
host port so an on-host ingress (e.g. a Cloudflare tunnel reaching
127.0.0.1:<port>) can route to them without CNI/bridge plumbing.
The backend (kamaji) is responsible for guarding this: host
networking is only honoured for tier == "infra" workloads; a
non-infra workload that sets the annotation is rejected at deploy. See
validate_spec_for_constable.
Sourcepub fn effective_archetype(&self) -> LifecycleArchetype
pub fn effective_archetype(&self) -> LifecycleArchetype
Resolve the lifecycle archetype (R572-F1 / W244): the explicit
Self::archetype if set, otherwise the pre-R572 inference from
volumes/restart_policy this field replaces.
This is the one seam callers should use to ask “can I kill and
reschedule this?” — it is intentionally the only place that
implements the fallback, so behavior for pre-existing specs (no
archetype on disk) is identical to what it was before this field
existed. Consumers (reconciler R572-F4, scheduler R572-F5) branch on
the return value; this crate does not itself change any reconciler or
scheduler behavior.
Sourcepub fn fq_mesh_identity(&self) -> String
pub fn fq_mesh_identity(&self) -> String
Fully-qualified mesh identity <tenant>/<namespace>/<name> (W206 /
R558-F3), where <name> is this workload’s MeshExpose::identity.
Within a tenant, workloads still address each other by the short
identity (namespace disambiguates only on collision); the FQN is what
makes the identity unambiguous across tenants and is exactly what a
MeshPeer::CrossTenant grant names.
Sourcepub fn requires_taint(&self) -> Option<&str>
pub fn requires_taint(&self) -> Option<&str>
The taint this workload requires its node to carry, if any (R594-F2 / W267 sovereign public ingress).
Opt-in via annotations["yah.placement.requires-taint"] = "<taint name>" (see REQUIRES_TAINT_ANNOTATION) — same annotation-based,
zero-blast-radius shape as Self::wants_host_network, chosen so
declaring this requirement does not force a struct-literal edit at
every existing WorkloadSpec { .. } construction site the way a new
plain field would (see R572-F1’s handoff: ~26 sites for one field).
This only declares the requirement — nothing matches it yet. The
taint itself doesn’t exist on the machine-TOML side until
R572-F3 adds a taints list there, and
nothing enforces repel-unless-tolerate placement until
R572-F5’s scheduler lands. Until then this
is inert metadata a future scheduler can read.
The public-ingress appliance (W267) is the first user: a
kind = "container" workload with archetype = Some(LifecycleArchetype::Appliance) and
requires_taint() == Some(PUBLIC_IP_TAINT), so yubaba may one day
place it only on machines carrying the "public-ip" taint and kamaji
supervises it like any other container (no new Workload variant —
see Workload::Container’s doc comment).
Trait Implementations§
Source§impl Clone for WorkloadSpec
impl Clone for WorkloadSpec
Source§fn clone(&self) -> WorkloadSpec
fn clone(&self) -> WorkloadSpec
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for WorkloadSpec
impl Debug for WorkloadSpec
Source§impl<'de> Deserialize<'de> for WorkloadSpec
impl<'de> Deserialize<'de> for WorkloadSpec
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for WorkloadSpec
impl PartialEq for WorkloadSpec
Source§impl Serialize for WorkloadSpec
impl Serialize for WorkloadSpec
impl StructuralPartialEq for WorkloadSpec
Source§impl TS for WorkloadSpec
impl TS for WorkloadSpec
Source§type WithoutGenerics = WorkloadSpec
type WithoutGenerics = WorkloadSpec
WithoutGenerics should just be Self.
If the type does have generic parameters, then all generic parameters must be replaced with
a dummy type, e.g ts_rs::Dummy or (). The only requirement for these dummy types is that
EXPORT_TO must be None. Read moreSource§type OptionInnerType = WorkloadSpec
type OptionInnerType = WorkloadSpec
std::option::Option<T>, then this associated type is set to T.
All other implementations of TS should set this type to Self instead.Source§fn docs() -> Option<String>
fn docs() -> Option<String>
TS is derived, docs are
automatically read from your doc comments or #[doc = ".."] attributesSource§fn decl_concrete(cfg: &Config) -> String
fn decl_concrete(cfg: &Config) -> String
TS::decl().
If this type is not generic, then this function is equivalent to TS::decl().Source§fn decl(cfg: &Config) -> String
fn decl(cfg: &Config) -> String
type User = { user_id: number, ... }.
This function will panic if the type has no declaration. Read moreSource§fn inline(cfg: &Config) -> String
fn inline(cfg: &Config) -> String
{ user_id: number }.
This function will panic if the type cannot be inlined.Source§fn inline_flattened(cfg: &Config) -> String
fn inline_flattened(cfg: &Config) -> String
Source§fn visit_generics(v: &mut impl TypeVisitor)where
Self: 'static,
fn visit_generics(v: &mut impl TypeVisitor)where
Self: 'static,
Source§fn output_path() -> Option<PathBuf>
fn output_path() -> Option<PathBuf>
T should be exported, relative to the output directory.
The returned path does not include any base directory. Read moreSource§fn visit_dependencies(v: &mut impl TypeVisitor)where
Self: 'static,
fn visit_dependencies(v: &mut impl TypeVisitor)where
Self: 'static,
Source§fn dependencies(cfg: &Config) -> Vec<Dependency>where
Self: 'static,
fn dependencies(cfg: &Config) -> Vec<Dependency>where
Self: 'static,
Source§fn export(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
fn export(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
TS::export_all. Read moreSource§fn export_all(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
fn export_all(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
TS::export. Read more