pub struct CloudConfig {
pub workspace_root: PathBuf,
pub machines: Vec<MachineConfig>,
pub providers: Vec<ProviderConfig>,
pub machine_origins: BTreeMap<String, InfraOrigin>,
pub provider_origins: BTreeMap<String, InfraOrigin>,
pub services: BTreeMap<String, ServiceWithMirrors>,
pub domains: BTreeMap<String, DomainConfig>,
pub legacy_mirrors: Vec<LegacyMirrorConfig>,
pub workloads: Vec<WorkloadConfig>,
pub topology: TopologyConfig,
pub legacy_services: Vec<LegacyServiceConfig>,
}Expand description
All cloud config loaded from a workspace root (the parent of .yah/).
Reads two trees:
.yah/infra/—machines/,providers/.yah/services/<svc>/—service.toml+mirrors/<env>.toml
Pre-R215 fields (legacy_mirrors, legacy_services, workloads,
topology) are still populated from .yah/cloud/ when present so
pre-R215 callers (compose.rs, bucket commands) keep compiling — they
just see empty collections in a post-B1 workspace where the legacy
data was deleted. These fields are scheduled for removal in B3-T3.
Fields§
§workspace_root: PathBufWorkspace root that was loaded — useful for path-resolving
component references on a ServiceComponent.
machines: Vec<MachineConfig>.yah/infra/machines/<name>.toml
providers: Vec<ProviderConfig>.yah/infra/providers/<id>.toml
machine_origins: BTreeMap<String, InfraOrigin>Provenance for every entry in machines that came from a linked
.yah/infra/sources.toml source rather than this camp’s own
.yah/infra/machines/ (R615-F2 / W274). Keyed by
MachineConfig::name; a name absent here is camp-local. Empty from
CloudConfig::load_from_config_dir — see its doc for why sources
don’t apply to multi-root sibling trees.
provider_origins: BTreeMap<String, InfraOrigin>Same as machine_origins, keyed by
ProviderConfig::id.
services: BTreeMap<String, ServiceWithMirrors>.yah/services/<svc>/ — service.toml plus mirrors/
domains: BTreeMap<String, DomainConfig>.yah/domains/<name>.toml — public-facing routing manifests
(R347). Single file per domain; no nested per-env tree because
domains themselves aren’t projected onto infra — they describe
how a Worker bundle ingresses requests onto services.
legacy_mirrors: Vec<LegacyMirrorConfig>Legacy mirrors from .yah/cloud/mirrors/.
workloads: Vec<WorkloadConfig>Workloads from .yah/cloud/workloads/*.toml (R092-F1 schema).
topology: TopologyConfigTopology from .yah/cloud/topology.toml (mirror→machine assignments).
legacy_services: Vec<LegacyServiceConfig>Legacy services from .yah/cloud/services/*.toml (pre-R092 layout).
Implementations§
Source§impl CloudConfig
impl CloudConfig
Sourcepub fn load(workspace_root: &Path) -> Result<Self>
pub fn load(workspace_root: &Path) -> Result<Self>
Load all cloud config rooted at workspace_root (the parent of .yah/).
Reads the R215+ tree (.yah/infra/, .yah/services/<svc>/) eagerly
and the pre-R215 .yah/cloud/ tree opportunistically. Returns Err
immediately if any TOML fails to parse or a workload TOML fails
shape validation; the error includes the file path and field path.
Cross-ref validation runs after both trees finish loading: every
mirror.providers.X.use = "<id>" must resolve to a real provider
declared under .yah/infra/providers/.
Sourcepub fn load_from_config_dir(
config_dir: &Path,
workspace_root: &Path,
) -> Result<Self>
pub fn load_from_config_dir( config_dir: &Path, workspace_root: &Path, ) -> Result<Self>
Load the R215+ tree (infra/, services/, domains/) rooted at an
arbitrary config directory instead of the hardcoded .yah/. This is the
building block for multi-root deployments (W206 config layout (b), sibling
.noisetable/ trees) — see crate::multi_root. Part of R558-F4.
config_dir is the .X/ directory itself (e.g. <parent>/.noisetable);
workspace_root remains the camp dir (the config dir’s parent) so a
component’s path reference resolves against the same tree the classic
CloudConfig::load uses. The legacy .yah/cloud/ reads are skipped —
multi-root deployments are post-R215 by construction — so legacy_*,
workloads, and topology come back empty. Machines are read from
config_dir/infra/machines directly (sibling trees declare their own
inventory or none).
R615-F2 decision, explicit rather than silent: sources.toml overlay
does NOT apply here. This function
exists specifically because a multi-root sibling tree (W206 layout
(b), e.g. .noisetable/) is a second config root inside the same
camp, not a second camp — config_dir is already wherever the
caller decided this tree’s infra lives, and .yah/infra/sources.toml
(singular, tied to paths::infra_dir(workspace_root)) has no
well-defined meaning for an arbitrary config_dir that isn’t that
path. A sibling tree that wants borrowed infra declares its own
sources.toml under whichever root actually calls
CloudConfig::load for it; machine_origins/provider_origins
come back empty here, not wrong — there is nothing to overlay.
Sourcepub fn domain(&self, name: &str) -> Option<&DomainConfig>
pub fn domain(&self, name: &str) -> Option<&DomainConfig>
Look up a domain manifest by name (file stem under .yah/domains/).
pub fn machine(&self, name: &str) -> Option<&MachineConfig>
Sourcepub fn provider(&self, id: &str) -> Option<&ProviderConfig>
pub fn provider(&self, id: &str) -> Option<&ProviderConfig>
Look up a provider by id (matches provider.id, not the file stem).
Sourcepub fn service(&self, name: &str) -> Option<&ServiceWithMirrors>
pub fn service(&self, name: &str) -> Option<&ServiceWithMirrors>
Look up a service by name (matches service.toml’s name field).
Sourcepub fn legacy_mirror(&self, camp: &str) -> Option<&LegacyMirrorConfig>
pub fn legacy_mirror(&self, camp: &str) -> Option<&LegacyMirrorConfig>
Look up a legacy mirror by camp name (pre-R215 .yah/cloud/mirrors/).
pub fn workload(&self, name: &str) -> Option<&WorkloadConfig>
Sourcepub fn machines_in_group(&self, group: &str) -> Vec<&MachineConfig>
pub fn machines_in_group(&self, group: &str) -> Vec<&MachineConfig>
Every machine declaring sovereign_group == group, in declaration order.
W305/R742-F3. A sovereign group has no file of its own — it exists only
as the set of machines that name the same string — so “which boxes are
the dev cluster” has to be derived, and before this it was not derived
anywhere: yah cloud rollout plan still takes a hand-listed
--voter us-west-011 --voter us-west-013 … for a fact the machine TOMLs
already state (W314 gap 1).
This is not placement. Resolving a group to its members is a
lookup, and it stays outside RequiredSpec on purpose — see
MachineConfig::sovereign_group. migrate calls this to pick the
candidate set it then admits a workload against; nothing here filters
scheduling, and adding sovereign_group to matches would still be the
category error that doc warns about.
An empty result means no machine declares group, which is
indistinguishable from a typo — callers should say so with
Self::declared_sovereign_groups rather than reporting “no
candidates”.
Sourcepub fn declared_sovereign_groups(&self) -> Vec<&str>
pub fn declared_sovereign_groups(&self) -> Vec<&str>
Every distinct sovereign_group declared by any machine, sorted.
Exists so a bad --to names the real vocabulary instead of complaining
abstractly — the same fail-loud shape taint_effect’s legal-key list
gives check_inert_taints. Standalone machines (None) contribute
nothing: “in no group” is not a group you can migrate to.
Sourcepub fn resolve_machine(&self, req: &RequiredSpec) -> Result<&MachineConfig>
pub fn resolve_machine(&self, req: &RequiredSpec) -> Result<&MachineConfig>
F16 placement v1: the first machine satisfying every hard axis of req
(region/zone/provider membership + mesh_tags superset). Declaration order
in .yah/infra/machines/ decides ties — deterministic-greedy, no
backtracking. A fully-unconstrained req matches the first machine.
Fails loud with the constraint summary and the candidate machine names
when nothing matches, so yah cloud apply surfaces why placement
failed instead of a silent empty set.
F16 placement: first machine whose mesh_tags is a superset of
required. Declaration order in .yah/infra/machines/ decides ties.
Empty required matches the first machine; callers should treat
empty-required as “no constraint” and skip this lookup.
Back-compat thin wrapper over CloudConfig::resolve_machine for the
mesh-tags-only call sites that predate the topology axes.
Sourcepub fn admit_workload(&self, ws: &WorkloadSpec) -> Result<&MachineConfig>
pub fn admit_workload(&self, ws: &WorkloadSpec) -> Result<&MachineConfig>
Admission: resolve the target machine for a remote WorkloadSpec,
honoring the R594 mesh-tag node-selector annotation
(velveteen_exec::remote::NODE_SELECTOR_MESH_TAGS_ANNOTATION =
yah.node-selector.mesh-tags, comma-joined).
The producer side (velveteen_exec::remote::build_workload_spec, R594) writes
TaskLocation::RemoteAny.mesh_tags — e.g. [tag:build-worker, arch:x86]
from [qed::platform::build_worker_mesh_tags] — into the workload’s
annotations. This is the consumer: candidates are restricted to machines
whose mesh_tags are a superset of the requested set, so an amd64
build lands on the arch:x86 build-worker (us-west-002) and an arm64
build on a arch:arm Pi5. Declaration order in .yah/infra/machines/
breaks ties.
An absent or empty annotation means “no mesh-tag constraint” — pre-R594
behavior (any node), matching RequiredSpec::is_unconstrained.
This is the single admission seam: R572-F5 extends it with the capacity
floor (workload request fits node allocatable−committed) and taint
repulsion/affinity by enriching RequiredSpec::matches /
Self::resolve_machine. Do not fork a second selector.
Sourcepub fn admit_workload_in_group(
&self,
ws: &WorkloadSpec,
group: &str,
) -> Result<&MachineConfig>
pub fn admit_workload_in_group( &self, ws: &WorkloadSpec, group: &str, ) -> Result<&MachineConfig>
Self::admit_workload restricted to the machines of one sovereign
group (W305/R742-F3, yah cloud migrate --to <group>).
Same RequiredSpec, same RequiredSpec::matches, same
declaration-order tie-break — only the candidate set differs. That is
the whole reason this is a narrowing of the admission seam rather than a
second selector: a workload that cannot be scheduled onto a group’s
boxes must fail here for exactly the reason it would fail anywhere else,
and no-appliance on the dev Pis (W305 finding 2) is precisely the case
that must not be silently routed around by a migration verb.
Err when the group has no members or when no member admits ws; the
two are different mistakes, so callers wanting to tell them apart should
check Self::machines_in_group first.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for CloudConfig
impl RefUnwindSafe for CloudConfig
impl Send for CloudConfig
impl Sync for CloudConfig
impl Unpin for CloudConfig
impl UnsafeUnpin for CloudConfig
impl UnwindSafe for CloudConfig
Blanket Implementations§
impl<T> Allocation for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> ErasedDestructor for Twhere
T: 'static,
impl<T> Fruit for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more