Skip to content

Args

An element can be a function with named args. yen fills the args it knows:

ArgValue
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
scopethe scope itself
anything else (pkgs, config)stays open: the element is still a function

A load balancer reads the addresses that its environment collects:

modules/fleet.nix
{ 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;
}
modules/aspects.nix
{ 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;
};
};
}
result
{
lb = [
{
at = "fleet:main/environment:prod/host:lb";
backends = [
"10.0.0.2"
"10.0.0.3"
];
cluster = "none";
env = "prod";
}
];
}

{ cluster ? null, ... } makes an arg optional. Without a default, an arg with no answer means the element is not given, and errors says why:

variants/missing.nix
# `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;
};
}
result
[
{
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.

variants/open.nix
# `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;
};
};
}
Contribute Community Sponsor