Scopes and spawns
A scope is one instance at one place in the tree. A kind says which instances live under
it with spawns.<kind>. This file builds a tree of environments, hosts and users:
# `spawns.<kind>` gives the children of a scope: a function of the parent scope.{ yen, ... }:{ yen.kinds.fleet.spawns.environment = _: yen.environment; # (1) yen.kinds.environment.spawns.host = yen.lib.pointing "environment" yen.host; # (2) yen.kinds.host = { fields.environment = yen.kinds.environment; spawns.user = yen.lib.pointing "hosts" yen.user; # (3) }; yen.kinds.user.fields.hosts = yen.lib.listOf yen.kinds.host; # refs to the hosts the user logs in to
yen.fleet.main = { };}spawns.<kind> is a function of the parent scope. It gives the instances that live under it.
(1) makes fleet spawn every environment. fleet is the root: no kind spawns it.
(2) and (3) find the children by a ref field. yen.lib.pointing "hosts" yen.user gives the users whose
hosts field holds the parent scope, as one ref or in a list.
The root fleet has the instance yen.fleet.main, and this data fills the tree:
{ yen, ... }:{ yen.environment.prod = { }; yen.environment.lab = { }; yen.host.web.environment = yen.environment.prod; yen.host.db.environment = yen.environment.prod; yen.host.box.environment = yen.environment.lab; yen.user.alice.hosts = [ yen.host.web yen.host.box ]; # (4) one user, two scopes yen.user.bob.hosts = [ yen.host.db ];}(4) gives one user two hosts, so that user gets two scopes.
The tree comes from the data. Nothing lists the children by hand:
flowchart TD n6c4c773b6d["fleet main"] nf631382c77["environment lab"] nc33ee814ad["host box"] n86d1c002a8["user alice"] n4a308930d1["environment prod"] n4019172c9d["host web"] n8bd47cb553["user alice"] nc60807bd8b["host db"] n614abf7a41["user bob"] n6c4c773b6d --> nf631382c77 nf631382c77 --> nc33ee814ad nc33ee814ad --> n86d1c002a8 n6c4c773b6d --> n4a308930d1 n4a308930d1 --> n4019172c9d n4019172c9d --> n8bd47cb553 n4a308930d1 --> nc60807bd8b nc60807bd8b --> n614abf7a41
Identity is a path
Section titled “Identity is a path”The id of a scope is its path: fleet:main/environment:prod/host:web/user:alice.
One instance can live in several places. alice has two scopes, one under each of her hosts.
Names need not be unique.
result.scopes gives the scopes in order:
[ "fleet:main" "fleet:main/environment:lab" "fleet:main/environment:lab/host:box" "fleet:main/environment:lab/host:box/user:alice" "fleet:main/environment:prod" "fleet:main/environment:prod/host:web" "fleet:main/environment:prod/host:web/user:alice" "fleet:main/environment:prod/host:db" "fleet:main/environment:prod/host:db/user:bob"]A parent comes before its children. Siblings sort by the order sink (default 0), then by name.
This aspect gives the host web an order of -1:
# Siblings sort by the `order` sink (default 0), then by name. A parent comes before its children.{ yen, ... }:{ yen.aspects.web-first = { at = yen.host.web; order = -1; };}So web comes before db.
Read the tree inside the fleet
Section titled “Read the tree inside the fleet”An aspect can read the tree. This example gives the scope ids as a flake output:
# The flake gives the scope ids. An aspect asks the tree for the whole fleet (`graph = true`) to read them.{ yen, ... }:let whole.graph = true; # (1) a tree request: the whole fleet fleetOf = tree: tree.select whole; # the whole fleet, with its datain{ yen.kinds.fleet.sinks.out.reduce = yen.lib.reduce.merge; yen.flake = { out, ... }: out; yen.aspects.tree = { at = yen.kinds.fleet; cycle = { tree, ... }: { tree = [ whole ]; out = [ { scopes = map (s: s.id) (fleetOf tree).lib.graph.scopes; # (2) } ]; }; };}(1) is a tree request: graph = true asks for the whole fleet. (2) reads the scopes of the answer.
The cycle page explains tree requests.