Args
An element can be a function with named args. yen fills the args it knows:
| Arg | Value |
|---|---|
a kind name (host, environment) | the nearest ancestor-or-self scope of that kind |
a sink name (backends) | the reduced value of that sink at its nearest receiver |
scope | the scope itself |
anything else (pkgs, config) | stays open: the element is still a function |
A load balancer reads the addresses that its environment collects:
{ yen, lib, ... }:let under = s: lib.filterAttrs (_: h: yen.lib.same h.environment s) yen.host;in{ yen.kinds.fleet = { spawns.environment = _: yen.environment; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.environment = { spawns.host = under; sinks.backends = { }; # hosts give their address; a wall: it stays at the environment }; yen.kinds.host = { fields = { environment = yen.kinds.environment; addr = lib.types.str; }; sinks.proxy.out = { host, proxy, ... }: { proxy.${host.name} = proxy; }; };
yen.kinds.cluster.fields = { }; # a kind that no scope here is under
yen.fleet.main = { }; yen.environment.prod = { }; yen.host.lb = { environment = yen.environment.prod; addr = "10.0.0.1"; }; yen.host.web-1 = { environment = yen.environment.prod; addr = "10.0.0.2"; }; yen.host.web-2 = { environment = yen.environment.prod; addr = "10.0.0.3"; }; yen.flake = { out, ... }: out;}{ yen, ... }:{ yen.aspects.backend = { at = yen.lib.all [ yen.kinds.host (s: s.name != "lb") ]; backends = { host, ... }: host.addr; # a kind arg: the nearest host }; yen.aspects.balancer = { at = yen.host.lb; # a sink arg: the reduced value at its nearest receiver (the environment); # `scope` is the scope itself; `cluster ? null` is optional, and this fleet has no cluster proxy = { environment, backends, scope, cluster ? null, ... }: { inherit backends; at = scope.id; env = environment.name; cluster = if cluster == null then "none" else cluster.name; }; };}{ lb = [ { at = "fleet:main/environment:prod/host:lb"; backends = [ "10.0.0.2" "10.0.0.3" ]; cluster = "none"; env = "prod"; } ];}Optional and missing args
Section titled “Optional and missing args”{ cluster ? null, ... } makes an arg optional. Without a default, an arg with no answer means the element
is not given, and errors says why:
# `cluster` without a default: no answer at lb, so the element is not given.{ yen, ... }:{ yen.aspects.cluster-name = { at = yen.host.lb; proxy = { cluster, ... }: cluster.name; };}[ { class = "fixable"; code = "error"; error = "aspect cluster-name: arg `cluster` has no answer"; from = "fleet:main/environment:prod/host:lb"; path = [ ]; }]Open args: modules for NixOS and home-manager
Section titled “Open args: modules for NixOS and home-manager”Args that yen does not know stay open. This is how a nixos element is a NixOS module: yen fills host,
and NixOS fills pkgs, config and lib later.
# `pkgs` is not a kind, a sink or `scope`: it stays open. The element is a module function.{ yen, ... }:{ yen.aspects.open = { at = yen.host.lb; proxy = { host, pkgs, ... }: { name = host.name; shell = pkgs.bash; }; };}