Skip to content

Instances and refs

A kind is also a type. A field of type yen.kinds.environment holds a ref to an environment instance: yen.environment.prod, never the string "prod".

modules/kinds.nix
# A kind is a type too: a field of kind `environment` holds a ref to an environment instance.
# Nothing spawns `environment`, so it is a data kind: its instances have no scope.
{ yen, lib, ... }:
{
yen.kinds.environment.fields.domain = lib.types.str;
yen.kinds.host.fields = {
environment = yen.kinds.environment;
backup = yen.lib.maybe yen.kinds.host; # a ref to another host, or null
};
}
modules/instances.nix
{ yen, ... }:
{
yen.environment.prod.domain = "example.com";
yen.environment.lab.domain = "lab.example.com";
yen.host.web = {
environment = yen.environment.prod;
backup = yen.host.db;
};
yen.host.db = {
environment = yen.environment.prod;
backup = yen.host.web;
}; # refs can point both ways
yen.host.test.environment = yen.environment.lab;
}

Refs are lazy, so two hosts can point at each other. There is no cycle error. Follow a ref like an attrset, and compare two instances with yen.lib.same:

modules/report.nix
{ yen, lib, ... }:
{
yen.aspects.report = {
at = yen.kinds.host;
report =
{ host, ... }:
{
${host.name} = {
fqdn = "${host.name}.${host.environment.domain}"; # follow a ref, read its fields
backup = if host.backup == null then null else host.backup.name;
# `same` compares two instances by kind and name
peers = lib.attrNames (
lib.filterAttrs (n: h: n != host.name && yen.lib.same h.environment host.environment) yen.host
);
};
};
};
}
result
{
db = {
backup = "web";
fqdn = "db.example.com";
peers = [
"web"
];
};
test = {
backup = null;
fqdn = "test.lab.example.com";
peers = [ ];
};
web = {
backup = "db";
fqdn = "web.example.com";
peers = [
"db"
];
};
}

environment is a data kind: no kind spawns it. Its instances have no place in the scope tree, and refs to them still work. The tree has the fleet and the hosts only:

flowchart TD
  n6c4c773b6d["fleet main"]
  nbe39bbfa9b["host db"]
  n493d96ad82["host test"]
  n0f3c4939ab["host web"]
  n6c4c773b6d --> nbe39bbfa9b
  n6c4c773b6d --> n493d96ad82
  n6c4c773b6d --> n0f3c4939ab

Groups, channels and secrets are good data kinds. Scopes shows how a kind gets a place in the tree.

A name where a ref goes is an error record, with the fix:

variants/string-ref.nix
# A name where a ref goes.
{ yen, ... }:
{
yen.host.cache.environment = "prod";
}
result
[
{
class = "fixable";
code = "ref";
error = "environment wants a environment ref";
fix = "use yen.environment.<name>, not a string";
from = "host:cache";
got = "prod";
path = [
"environment"
];
want = "a environment ref";
}
]
Contribute Community Sponsor