test the archetype module import in the meta flake render; update archetypes doc
This commit is contained in:
parent
b6b568e97e
commit
88053f437c
3 changed files with 80 additions and 20 deletions
|
|
@ -7,8 +7,8 @@ resource caps and inference fields from it, and you can still change each
|
||||||
field before you create the agent.
|
field before you create the agent.
|
||||||
|
|
||||||
An agent created from an archetype has no `agent-configs/<name>` repo of its
|
An agent created from an archetype has no `agent-configs/<name>` repo of its
|
||||||
own. Its hive builds it from the archetype's module, so every agent of that
|
own. The swarm's `internal/meta` flake builds it from the archetype's module,
|
||||||
archetype runs the same configuration. To change what it runs, change the
|
so every agent of that archetype runs the same configuration. To change what it runs, change the
|
||||||
archetype. Picking "none" creates a custom agent, with its own config repo.
|
archetype. Picking "none" creates a custom agent, with its own config repo.
|
||||||
|
|
||||||
## The built-in archetypes
|
## The built-in archetypes
|
||||||
|
|
@ -41,11 +41,13 @@ hyperhiveArchetypes.reviewer = {
|
||||||
- Every archetype needs a `description`. `permissions`, `limits` and
|
- Every archetype needs a `description`. `permissions`, `limits` and
|
||||||
`inference` are optional and take the shapes `POST /api/agents` takes. The swarm ignores
|
`inference` are optional and take the shapes `POST /api/agents` takes. The swarm ignores
|
||||||
other keys in `meta`.
|
other keys in `meta`.
|
||||||
- `module` is a NixOS module for the agent's container. A hive imports it
|
- `module` is a NixOS module for the agent's container. The meta flake
|
||||||
into each agent created from the archetype.
|
imports it into each agent created from the archetype.
|
||||||
|
|
||||||
Today the swarm reads one flake: the hyperhive source the
|
Today the swarm reads one flake: the hyperhive source the
|
||||||
[builder](builder.md)'s host runs, so only the built-ins appear.
|
[builder](builder.md)'s host runs, so only the built-ins appear. The meta
|
||||||
|
flake has one archetype source too, its own `hyperhive` input, and refuses
|
||||||
|
an archetype from any other source name.
|
||||||
|
|
||||||
## How the list stays current
|
## How the list stays current
|
||||||
|
|
||||||
|
|
@ -110,23 +112,31 @@ Deploy `swarm-nats-auth` before the hives: every provision reads the
|
||||||
agent's key, and a hive whose queue connection predates the grant gets a
|
agent's key, and a hive whose queue connection predates the grant gets a
|
||||||
refused read, so its `Provision` nodes fail until it reconnects.
|
refused read, so its `Provision` nodes fail until it reconnects.
|
||||||
|
|
||||||
The hive reads the agent's key when it provisions the agent and on every
|
swarm-controller renders `internal/meta`'s `flake.nix` with the
|
||||||
rebuild, and keeps the last value read in
|
`hive-meta-flake` crate, and reads each agent's archetype from the
|
||||||
`/var/lib/hyperhive/swarm-agent-archetypes.json`, so the meta flake renders
|
`agent-archetype` bucket to do so. For an archetype agent the render:
|
||||||
without the queue. When the read fails, a rebuild keeps the value
|
|
||||||
stored there. For an archetype agent the hive:
|
|
||||||
|
|
||||||
- seeds no `proposed` or `applied` repo, and gives `internal/meta` no
|
- adds no `agent-<name>` input and reads no config repo, and lists the agent
|
||||||
`agent-<name>` input. `mkAgent` imports
|
as `mkAgent { …; archetype = { source = "hyperhive"; name = "<name>"; }; }`;
|
||||||
`hyperhive.hyperhiveArchetypes.<name>.module` in place of the agent's own
|
- makes `mkAgent` import `hyperhive.hyperhiveArchetypes.<name>.module` in
|
||||||
flake, and sets `HIVE_ARCHETYPE` in the harness environment;
|
place of the agent's own flake, and set `HIVE_ARCHETYPE` in the harness
|
||||||
|
environment.
|
||||||
|
|
||||||
|
The hive builds from `internal/meta` alone. It reads the agent's key when it
|
||||||
|
provisions the agent and on every rebuild, and keeps the last value read in
|
||||||
|
`/var/lib/hyperhive/swarm-agent-archetypes.json`. It takes the head of
|
||||||
|
`internal/meta` only when that `flake.nix` lists the agent at the archetype
|
||||||
|
it has now. When the read fails, a rebuild keeps the stored value. For an
|
||||||
|
archetype agent the hive also:
|
||||||
|
|
||||||
|
- seeds no `proposed` or `applied` repo;
|
||||||
- ignores the `rev` of a swarm deploy request;
|
- ignores the `rev` of a swarm deploy request;
|
||||||
- mounts an empty `/agents/<name>/config/`.
|
- mounts an empty `/agents/<name>/config/`.
|
||||||
|
|
||||||
The agent's system prompt says it has no config repo and names its
|
The agent's system prompt says it has no config repo and names its
|
||||||
archetype.
|
archetype.
|
||||||
|
|
||||||
The hive takes the module from its own `hyperhive` input. When that input
|
The module comes from the `hyperhive` input that `internal/meta` pins, not
|
||||||
predates the archetype, the agent's build fails with
|
from the hive's own. When that pin predates the archetype, the agent's build
|
||||||
`this hive's hyperhive input has no archetype <name>`. Update the hive's
|
fails with `this hive's hyperhive input has no archetype <name>; update that
|
||||||
`hyperhive` input to fix it.
|
input`. Update the `hyperhive` pin in `internal/meta` to fix it.
|
||||||
|
|
|
||||||
|
|
@ -194,8 +194,9 @@
|
||||||
nixosConfigurations =
|
nixosConfigurations =
|
||||||
let
|
let
|
||||||
# These two configs are what hive-c0re extends per agent
|
# These two configs are what hive-c0re extends per agent
|
||||||
# (meta.rs's `mkAgent` does `<base>.extendModules { … }`, so
|
# (the `mkAgent` in hive-meta-flake's render does
|
||||||
# the built base and every container stay on one version).
|
# `<base>.extendModules { … }`, so the built base and every
|
||||||
|
# container stay on one version).
|
||||||
# Nothing deployment-specific may be set here: it would be
|
# Nothing deployment-specific may be set here: it would be
|
||||||
# inherited by every container on the hive and collide with
|
# inherited by every container on the hive and collide with
|
||||||
# the per-agent values hive-c0re renders. The agent modules
|
# the per-agent values hive-c0re renders. The agent modules
|
||||||
|
|
|
||||||
|
|
@ -617,6 +617,55 @@ mod tests {
|
||||||
assert!(!builds_agent_at(&flake, "ghost", Some(&reviewer()), None));
|
assert!(!builds_agent_at(&flake, "ghost", Some(&reviewer()), None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The text of `mkAgent`'s `let` binding `name`: from `name =` to the
|
||||||
|
/// next binding at the same indent (`;` ends it at line end).
|
||||||
|
fn binding<'a>(flake: &'a str, name: &str) -> &'a str {
|
||||||
|
let from = flake
|
||||||
|
.find(&format!("\n {name} =\n"))
|
||||||
|
.or_else(|| flake.find(&format!("\n {name} = ")))
|
||||||
|
.unwrap_or_else(|| panic!("no `{name}` binding in mkAgent"));
|
||||||
|
let rest = &flake[from + 1..];
|
||||||
|
let end = rest
|
||||||
|
.find("\n service =")
|
||||||
|
.unwrap_or_else(|| panic!("`{name}` is the last binding"));
|
||||||
|
&rest[..end]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn an_archetype_agent_s_module_is_the_archetype_source_s_module() {
|
||||||
|
let flake = rendered();
|
||||||
|
let module = binding(&flake, "agentModule");
|
||||||
|
assert!(
|
||||||
|
module.contains("archetypeSources.${archetype.source}"),
|
||||||
|
"the archetype branch picks its source by name: {module}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
module.contains(".hyperhiveArchetypes.${archetype.name}.module"),
|
||||||
|
"the archetype branch imports the archetype's module: {module}"
|
||||||
|
);
|
||||||
|
// The source it picks from must offer the hyperhive input.
|
||||||
|
assert!(
|
||||||
|
binding(&flake, "archetypeSources").contains("inherit hyperhive;"),
|
||||||
|
"the `hyperhive` archetype source"
|
||||||
|
);
|
||||||
|
// The module reaches the agent's system.
|
||||||
|
let modules = &flake[flake.find("modules = [").expect("a module list")..];
|
||||||
|
assert!(
|
||||||
|
modules
|
||||||
|
.trim_start_matches("modules = [")
|
||||||
|
.trim_start()
|
||||||
|
.starts_with("agentModule"),
|
||||||
|
"agentModule heads the extendModules list"
|
||||||
|
);
|
||||||
|
// The entry names the source and archetype the import reads.
|
||||||
|
assert!(
|
||||||
|
flake.contains("rev = mkAgent { name = \"rev\"; port = ")
|
||||||
|
&& flake
|
||||||
|
.contains(" archetype = { source = \"hyperhive\"; name = \"reviewer\"; }; };"),
|
||||||
|
"the archetype agent's entry"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn an_agent_whose_name_ends_another_s_is_not_that_agent() {
|
fn an_agent_whose_name_ends_another_s_is_not_that_agent() {
|
||||||
let flake = rendered();
|
let flake = rendered();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue