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.
|
||||
|
||||
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
|
||||
archetype runs the same configuration. To change what it runs, change the
|
||||
own. The swarm's `internal/meta` flake builds it from the archetype's module,
|
||||
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.
|
||||
|
||||
## The built-in archetypes
|
||||
|
|
@ -41,11 +41,13 @@ hyperhiveArchetypes.reviewer = {
|
|||
- Every archetype needs a `description`. `permissions`, `limits` and
|
||||
`inference` are optional and take the shapes `POST /api/agents` takes. The swarm ignores
|
||||
other keys in `meta`.
|
||||
- `module` is a NixOS module for the agent's container. A hive imports it
|
||||
into each agent created from the archetype.
|
||||
- `module` is a NixOS module for the agent's container. The meta flake
|
||||
imports it into each agent created from the archetype.
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -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
|
||||
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
|
||||
rebuild, and keeps the last value read in
|
||||
`/var/lib/hyperhive/swarm-agent-archetypes.json`, so the meta flake renders
|
||||
without the queue. When the read fails, a rebuild keeps the value
|
||||
stored there. For an archetype agent the hive:
|
||||
swarm-controller renders `internal/meta`'s `flake.nix` with the
|
||||
`hive-meta-flake` crate, and reads each agent's archetype from the
|
||||
`agent-archetype` bucket to do so. For an archetype agent the render:
|
||||
|
||||
- seeds no `proposed` or `applied` repo, and gives `internal/meta` no
|
||||
`agent-<name>` input. `mkAgent` imports
|
||||
`hyperhive.hyperhiveArchetypes.<name>.module` in place of the agent's own
|
||||
flake, and sets `HIVE_ARCHETYPE` in the harness environment;
|
||||
- adds no `agent-<name>` input and reads no config repo, and lists the agent
|
||||
as `mkAgent { …; archetype = { source = "hyperhive"; name = "<name>"; }; }`;
|
||||
- makes `mkAgent` import `hyperhive.hyperhiveArchetypes.<name>.module` in
|
||||
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;
|
||||
- mounts an empty `/agents/<name>/config/`.
|
||||
|
||||
The agent's system prompt says it has no config repo and names its
|
||||
archetype.
|
||||
|
||||
The hive takes the module from its own `hyperhive` input. When that input
|
||||
predates the archetype, the agent's build fails with
|
||||
`this hive's hyperhive input has no archetype <name>`. Update the hive's
|
||||
`hyperhive` input to fix it.
|
||||
The module comes from the `hyperhive` input that `internal/meta` pins, not
|
||||
from the hive's own. When that pin predates the archetype, the agent's build
|
||||
fails with `this hive's hyperhive input has no archetype <name>; update that
|
||||
input`. Update the `hyperhive` pin in `internal/meta` to fix it.
|
||||
|
|
|
|||
|
|
@ -194,8 +194,9 @@
|
|||
nixosConfigurations =
|
||||
let
|
||||
# These two configs are what hive-c0re extends per agent
|
||||
# (meta.rs's `mkAgent` does `<base>.extendModules { … }`, so
|
||||
# the built base and every container stay on one version).
|
||||
# (the `mkAgent` in hive-meta-flake's render does
|
||||
# `<base>.extendModules { … }`, so the built base and every
|
||||
# container stay on one version).
|
||||
# Nothing deployment-specific may be set here: it would be
|
||||
# inherited by every container on the hive and collide with
|
||||
# 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));
|
||||
}
|
||||
|
||||
/// 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]
|
||||
fn an_agent_whose_name_ends_another_s_is_not_that_agent() {
|
||||
let flake = rendered();
|
||||
|
|
|
|||
Loading…
Reference in a new issue