hive-forge: require HIVE_FORGE_URL, give agent login shells the forge env
This commit is contained in:
parent
aecde1dc75
commit
4440d8fb29
6 changed files with 171 additions and 87 deletions
|
|
@ -7,7 +7,7 @@ consistent error handling, exit codes, and JSON shapes. This is the
|
||||||
**only supported path** to the forge from inside an agent container;
|
**only supported path** to the forge from inside an agent container;
|
||||||
never `curl` it directly.
|
never `curl` it directly.
|
||||||
|
|
||||||
Reads credentials from the environment (`HIVE_FORGE_URL`,
|
Reads credentials from the environment (`HIVE_FORGE_URL`, required — no default,
|
||||||
`HYPERHIVE_STATE_DIR`); `-f/--forge <label>` retargets a
|
`HYPERHIVE_STATE_DIR`); `-f/--forge <label>` retargets a
|
||||||
swarm-UI-linked external forge account instead. The active repo
|
swarm-UI-linked external forge account instead. The active repo
|
||||||
resolves `-r/--repo` > the `origin` remote of the cwd's git checkout >
|
resolves `-r/--repo` > the `origin` remote of the cwd's git checkout >
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,6 @@ use reqwest::header::{
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
/// Default Forgejo URL when `HIVE_FORGE_URL` is unset.
|
|
||||||
const DEFAULT_URL: &str = "http://localhost:3000";
|
|
||||||
|
|
||||||
/// Bound on reaching the forge.
|
/// Bound on reaching the forge.
|
||||||
const HTTP_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
|
const HTTP_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
|
||||||
/// Whole-request budget for a small JSON call (an `/api/v1` GET, or the
|
/// Whole-request budget for a small JSON call (an `/api/v1` GET, or the
|
||||||
|
|
@ -395,9 +392,22 @@ pub fn index(n: u64) -> Result<i64> {
|
||||||
i64::try_from(n).with_context(|| format!("number {n} out of range"))
|
i64::try_from(n).with_context(|| format!("number {n} out of range"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The internal forge's base URL from the `HIVE_FORGE_URL` value, which has
|
||||||
|
/// no default: inside an agent container `localhost` is the agent itself.
|
||||||
|
fn forge_url(value: Option<String>) -> Result<String> {
|
||||||
|
match value {
|
||||||
|
Some(url) if !url.trim().is_empty() => Ok(url),
|
||||||
|
_ => bail!(
|
||||||
|
"hive-forge: HIVE_FORGE_URL is unset or empty — the forge URL comes from the \
|
||||||
|
hive config (services.hyperhive.swarm.forge.domain) and is set in every \
|
||||||
|
agent container; export it, or use -f/--forge <label> for a linked external forge"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve the `(base_url, token)` pair the client authenticates with.
|
/// Resolve the `(base_url, token)` pair the client authenticates with.
|
||||||
/// `None` (the default) resolves the internal forge exactly as before:
|
/// `None` (the default) resolves the internal forge exactly as before:
|
||||||
/// `HIVE_FORGE_URL` (default [`DEFAULT_URL`]) + `read_token()`.
|
/// `HIVE_FORGE_URL` (required, see [`forge_url`]) + `read_token()`.
|
||||||
/// `Some(label)` (from `-f/--forge <label>`) instead resolves an
|
/// `Some(label)` (from `-f/--forge <label>`) instead resolves an
|
||||||
/// external forge account linked in the swarm UI: the token comes from
|
/// external forge account linked in the swarm UI: the token comes from
|
||||||
/// `${HYPERHIVE_STATE_DIR}/forge-<label>-token` and the base URL from
|
/// `${HYPERHIVE_STATE_DIR}/forge-<label>-token` and the base URL from
|
||||||
|
|
@ -411,7 +421,7 @@ pub fn index(n: u64) -> Result<i64> {
|
||||||
/// nonsense/traversing path and surfacing a confusing file error later.
|
/// nonsense/traversing path and surfacing a confusing file error later.
|
||||||
pub(crate) fn resolve_credentials(forge_label: Option<&str>) -> Result<(String, String)> {
|
pub(crate) fn resolve_credentials(forge_label: Option<&str>) -> Result<(String, String)> {
|
||||||
let Some(label) = forge_label else {
|
let Some(label) = forge_label else {
|
||||||
let base = std::env::var("HIVE_FORGE_URL").unwrap_or_else(|_| DEFAULT_URL.to_owned());
|
let base = forge_url(std::env::var("HIVE_FORGE_URL").ok())?;
|
||||||
let token = read_token().context("read forge-token")?;
|
let token = read_token().context("read forge-token")?;
|
||||||
return Ok((base, token));
|
return Ok((base, token));
|
||||||
};
|
};
|
||||||
|
|
@ -595,7 +605,26 @@ fn check_status(resp: Response, op: &str) -> Result<Response> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{WebStatusError, disposition_filename, index, owner_repo_from_git_url, split_repo};
|
use super::{
|
||||||
|
WebStatusError, disposition_filename, forge_url, index, owner_repo_from_git_url, split_repo,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forge_url_is_the_value_as_is() {
|
||||||
|
assert_eq!(
|
||||||
|
forge_url(Some("http://forge.t.local".to_owned())).unwrap(),
|
||||||
|
"http://forge.t.local"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forge_url_unset_or_blank_names_the_variable() {
|
||||||
|
for v in [None, Some(String::new()), Some(" ".to_owned())] {
|
||||||
|
let err = forge_url(v).unwrap_err().to_string();
|
||||||
|
assert!(err.contains("HIVE_FORGE_URL is unset or empty"), "{err}");
|
||||||
|
assert!(err.contains("hive config"), "{err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn disposition_filename_reads_forgejos_real_header() {
|
fn disposition_filename_reads_forgejos_real_header() {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! `hive-forge` — typed CLI wrapper around the in-cluster Forgejo's
|
//! `hive-forge` — typed CLI wrapper around the in-cluster Forgejo's
|
||||||
//! REST API. Reads credentials from the environment:
|
//! REST API. Reads credentials from the environment:
|
||||||
//!
|
//!
|
||||||
//! `HIVE_FORGE_URL` — base URL, e.g. `http://localhost:3000`
|
//! `HIVE_FORGE_URL` — base URL (required), e.g. `http://forge.example.org`
|
||||||
//! `HYPERHIVE_STATE_DIR` — state dir; `forge-token` lives here
|
//! `HYPERHIVE_STATE_DIR` — state dir; `forge-token` lives here
|
||||||
//! `HIVE_FORGE_DEADLINE` — optional; whole seconds one invocation may run
|
//! `HIVE_FORGE_DEADLINE` — optional; whole seconds one invocation may run
|
||||||
//! before it exits with code 75 (default 900)
|
//! before it exits with code 75 (default 900)
|
||||||
|
|
|
||||||
57
hive-forge/tests/forge_url_required.rs
Normal file
57
hive-forge/tests/forge_url_required.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
//! `HIVE_FORGE_URL` has no default: unset or empty is a clear error naming
|
||||||
|
//! the variable, and a set value is the URL the client dials.
|
||||||
|
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
|
||||||
|
const UNSET_MSG: &str = "HIVE_FORGE_URL is unset or empty";
|
||||||
|
|
||||||
|
fn scratch_state_dir(tag: &str) -> std::path::PathBuf {
|
||||||
|
let ts = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.map_or(0, |d| d.as_nanos());
|
||||||
|
let dir = std::env::temp_dir().join(format!("hive-forge-url-required-{tag}-{ts}"));
|
||||||
|
std::fs::create_dir_all(&dir).expect("create scratch state dir");
|
||||||
|
std::fs::write(dir.join("forge-token"), "test-token\n").expect("seed forge-token");
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(tag: &str, url: Option<&str>) -> Output {
|
||||||
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_hive-forge"));
|
||||||
|
cmd.args(["-r", "o/r", "pr", "edit", "7", "--title", "t"])
|
||||||
|
.env("HYPERHIVE_STATE_DIR", scratch_state_dir(tag))
|
||||||
|
.env_remove("HIVE_FORGE_TOKEN_FILE")
|
||||||
|
.env_remove("HIVE_LABEL");
|
||||||
|
match url {
|
||||||
|
Some(u) => cmd.env("HIVE_FORGE_URL", u),
|
||||||
|
None => cmd.env_remove("HIVE_FORGE_URL"),
|
||||||
|
};
|
||||||
|
cmd.output().expect("run hive-forge")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unset_url_is_a_clear_error() {
|
||||||
|
let out = run("unset", None);
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||||
|
assert!(!out.status.success());
|
||||||
|
assert!(stderr.contains(UNSET_MSG), "stderr: {stderr}");
|
||||||
|
assert!(!stderr.contains("localhost"), "stderr: {stderr}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_url_is_a_clear_error() {
|
||||||
|
let out = run("empty", Some(""));
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||||
|
assert!(!out.status.success());
|
||||||
|
assert!(stderr.contains(UNSET_MSG), "stderr: {stderr}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Presence control: a set value passes the URL check and the client dials it
|
||||||
|
/// (nothing listens on port 1, so the failure is a connection error).
|
||||||
|
#[test]
|
||||||
|
fn set_url_is_used_as_is() {
|
||||||
|
let out = run("set", Some("http://127.0.0.1:1"));
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||||
|
assert!(!out.status.success());
|
||||||
|
assert!(!stderr.contains(UNSET_MSG), "stderr: {stderr}");
|
||||||
|
assert!(stderr.contains("127.0.0.1"), "stderr: {stderr}");
|
||||||
|
}
|
||||||
|
|
@ -208,9 +208,9 @@ pub fn render_flake(
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
push_inputs(&mut out, hyperhive_url, agents)?;
|
push_inputs(&mut out, hyperhive_url, agents)?;
|
||||||
push_mk_agent_open(&mut out);
|
push_mk_agent_open(&mut out, vars);
|
||||||
push_agent_service_options(&mut out, vars, github_disabled);
|
push_agent_service_options(&mut out, vars, github_disabled);
|
||||||
push_agent_environment(&mut out, vars);
|
push_agent_environment(&mut out);
|
||||||
push_agent_entries(&mut out, agents);
|
push_agent_entries(&mut out, agents);
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
@ -267,8 +267,10 @@ fn push_inputs(out: &mut String, hyperhive_url: &str, agents: &[AgentSpec]) -> R
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes `inputs`, opens `outputs` and the `mkAgent` function up to the
|
/// Closes `inputs`, opens `outputs` and the `mkAgent` function up to the
|
||||||
/// start of the per-agent module.
|
/// start of the per-agent module. Binds `agentEnv`, the one attrset the
|
||||||
fn push_mk_agent_open(out: &mut String) {
|
/// container-wide, login-shell and harness-service environments all take.
|
||||||
|
fn push_mk_agent_open(out: &mut String, vars: &[(&'static str, String)]) {
|
||||||
|
use std::fmt::Write as _;
|
||||||
out.push_str(
|
out.push_str(
|
||||||
" };\n outputs =\n { self, hyperhive, ... }@inputs:\n let\n mkAgent = { name, port, archetype ? null }:\n",
|
" };\n outputs =\n { self, hyperhive, ... }@inputs:\n let\n mkAgent = { name, port, archetype ? null }:\n",
|
||||||
);
|
);
|
||||||
|
|
@ -286,12 +288,27 @@ fn push_mk_agent_open(out: &mut String) {
|
||||||
).hyperhiveArchetypes.${archetype.name}.module
|
).hyperhiveArchetypes.${archetype.name}.module
|
||||||
or (throw "agent ${name}: this hive's ${archetype.source} input has no archetype ${archetype.name}; update that input");
|
or (throw "agent ${name}: this hive's ${archetype.source} input has no archetype ${archetype.name}; update that input");
|
||||||
service = "hive-agent";
|
service = "hive-agent";
|
||||||
|
# One attrset for `environment.variables` (login shells),
|
||||||
|
# `systemd.globalEnvironment` (every unit) and the harness service,
|
||||||
|
# so the three cannot disagree. The forwarded vars come from the host.
|
||||||
|
agentEnv = {
|
||||||
|
HIVE_LABEL = name;
|
||||||
|
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
||||||
|
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
for (var, val) in vars {
|
||||||
|
let escaped = val.replace('\\', "\\\\").replace('"', "\\\"");
|
||||||
|
let _ = writeln!(out, " {var} = \"{escaped}\";");
|
||||||
|
}
|
||||||
|
out.push_str(
|
||||||
|
r" };
|
||||||
in
|
in
|
||||||
base.extendModules {
|
base.extendModules {
|
||||||
modules = [
|
modules = [
|
||||||
agentModule
|
agentModule
|
||||||
{
|
{
|
||||||
"#,
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,8 +356,7 @@ fn push_agent_service_options(
|
||||||
|
|
||||||
/// The rest of the per-agent module: user, hostname, git identity, and the
|
/// The rest of the per-agent module: user, hostname, git identity, and the
|
||||||
/// container-wide and harness-service environments.
|
/// container-wide and harness-service environments.
|
||||||
fn push_agent_environment(out: &mut String, vars: &[(&'static str, String)]) {
|
fn push_agent_environment(out: &mut String) {
|
||||||
use std::fmt::Write as _;
|
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#" # The harness service inside the container runs as a
|
r#" # The harness service inside the container runs as a
|
||||||
# non-root unix user named after the agent (`damocles`,
|
# non-root unix user named after the agent (`damocles`,
|
||||||
|
|
@ -362,58 +378,15 @@ fn push_agent_environment(out: &mut String, vars: &[(&'static str, String)]) {
|
||||||
name = name;
|
name = name;
|
||||||
email = "${name}@hyperhive.local";
|
email = "${name}@hyperhive.local";
|
||||||
};
|
};
|
||||||
# Container-wide env: every service + co-process daemon can
|
# Container-wide env, all from `agentEnv`: every unit and daemon
|
||||||
# resolve the agent's durable state dir without hard-coding it.
|
# (the bash-task runner where `hive-forge` and `git` run included)
|
||||||
# `environment.variables` only writes /etc/environment (login
|
# resolves the state dir and the forwarded service URLs without
|
||||||
# shells); `systemd.globalEnvironment` is the analogue for
|
# redeclaring them. `environment.variables` is what login shells
|
||||||
# systemd units so forge-avatar-sync /
|
# read; `systemd.globalEnvironment` is the analogue for units.
|
||||||
# hive-matrix-daemon etc. can read `$HYPERHIVE_STATE_DIR`
|
environment.variables = agentEnv;
|
||||||
# without each service having to redeclare it.
|
systemd.globalEnvironment = agentEnv;
|
||||||
environment.variables = {
|
systemd.services.${service}.environment = agentEnv // {
|
||||||
HIVE_LABEL = name;
|
|
||||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
};
|
|
||||||
systemd.globalEnvironment = {
|
|
||||||
HIVE_LABEL = name;
|
|
||||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
// Forwarded vars (HIVE_FORGE_URL etc.) also go into globalEnvironment,
|
|
||||||
// not just the harness service env below, so EVERY service + shell in
|
|
||||||
// the container inherits them — crucially the bash-task runner (where
|
|
||||||
// `hive-forge` + `git` actually run), plus the matrix daemon, forge-avatar-sync
|
|
||||||
// and interactive shells. Scoped to the harness service alone they were
|
|
||||||
// invisible to bash tasks: harmless in shared netns (the localhost
|
|
||||||
// default works) but broken under isolation, where the in-cluster
|
|
||||||
// `forge.<domain>` URL is the only reachable path.
|
|
||||||
for (var, val) in vars {
|
|
||||||
let escaped = val.replace('\\', "\\\\").replace('"', "\\\"");
|
|
||||||
let _ = writeln!(out, " {var} = \"{escaped}\";");
|
|
||||||
}
|
|
||||||
out.push_str(
|
|
||||||
r" };
|
|
||||||
systemd.services.${service}.environment = {
|
|
||||||
HIVE_PORT = toString port;
|
HIVE_PORT = toString port;
|
||||||
HIVE_LABEL = name;
|
|
||||||
",
|
|
||||||
);
|
|
||||||
// Forwarded env vars, as the caller passed them. We copy whatever's set into
|
|
||||||
// each sub-agent's harness service env so the in-container surfaces
|
|
||||||
// (`identity.rs`, `forge_notify`) see a consistent view across the
|
|
||||||
// whole hive. Absent host-side env (option not set) → skip emission
|
|
||||||
// → in-container accessors fall back to None / defaults gracefully.
|
|
||||||
//
|
|
||||||
// - HIVE_FORGE_URL: agents poll this for Forgejo notifications.
|
|
||||||
// - HYPERHIVE_SWARM_NAME: human display name for the swarm.
|
|
||||||
for (var, val) in vars {
|
|
||||||
let escaped = val.replace('\\', "\\\\").replace('"', "\\\"");
|
|
||||||
let _ = writeln!(out, " {var} = \"{escaped}\";");
|
|
||||||
}
|
|
||||||
out.push_str(
|
|
||||||
r#" HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# Tells the harness the agent has no config repo of its own.
|
# Tells the harness the agent has no config repo of its own.
|
||||||
|
|
@ -580,6 +553,36 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Login shells, every unit and the harness service take the forwarded
|
||||||
|
/// vars from the one `agentEnv` attrset, so none can lack a value another has.
|
||||||
|
#[test]
|
||||||
|
fn forwarded_vars_reach_login_shells_units_and_the_harness_from_one_attrset() {
|
||||||
|
let vars = [
|
||||||
|
("HIVE_FORGE_URL", "http://forge.t.local".to_owned()),
|
||||||
|
("HYPERHIVE_SWARM_NAME", "t".to_owned()),
|
||||||
|
];
|
||||||
|
let archetype = ArchetypeRef {
|
||||||
|
source: "hyperhive".to_owned(),
|
||||||
|
name: "reviewer".to_owned(),
|
||||||
|
};
|
||||||
|
let flake = render_flake("u", &[spec("rev", Some(archetype), None)], &vars, false).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
flake
|
||||||
|
.matches("HIVE_FORGE_URL = \"http://forge.t.local\";")
|
||||||
|
.count(),
|
||||||
|
1,
|
||||||
|
"the value is written once, in agentEnv"
|
||||||
|
);
|
||||||
|
assert!(flake.contains("agentEnv = {\n HIVE_LABEL = name;"));
|
||||||
|
for sink in [
|
||||||
|
"environment.variables = agentEnv;",
|
||||||
|
"systemd.globalEnvironment = agentEnv;",
|
||||||
|
"systemd.services.${service}.environment = agentEnv // {",
|
||||||
|
] {
|
||||||
|
assert!(flake.contains(sink), "missing `{sink}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_rendered_flake_names_the_commit_each_input_is_pinned_to() {
|
fn a_rendered_flake_names_the_commit_each_input_is_pinned_to() {
|
||||||
let flake = rendered();
|
let flake = rendered();
|
||||||
|
|
|
||||||
37
hive-meta-flake/testdata/meta-flake.nix.in
vendored
37
hive-meta-flake/testdata/meta-flake.nix.in
vendored
|
|
@ -21,6 +21,14 @@
|
||||||
).hyperhiveArchetypes.${archetype.name}.module
|
).hyperhiveArchetypes.${archetype.name}.module
|
||||||
or (throw "agent ${name}: this hive's ${archetype.source} input has no archetype ${archetype.name}; update that input");
|
or (throw "agent ${name}: this hive's ${archetype.source} input has no archetype ${archetype.name}; update that input");
|
||||||
service = "hive-agent";
|
service = "hive-agent";
|
||||||
|
# One attrset for `environment.variables` (login shells),
|
||||||
|
# `systemd.globalEnvironment` (every unit) and the harness service,
|
||||||
|
# so the three cannot disagree. The forwarded vars come from the host.
|
||||||
|
agentEnv = {
|
||||||
|
HIVE_LABEL = name;
|
||||||
|
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
||||||
|
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
||||||
|
};
|
||||||
in
|
in
|
||||||
base.extendModules {
|
base.extendModules {
|
||||||
modules = [
|
modules = [
|
||||||
|
|
@ -46,28 +54,15 @@
|
||||||
name = name;
|
name = name;
|
||||||
email = "${name}@hyperhive.local";
|
email = "${name}@hyperhive.local";
|
||||||
};
|
};
|
||||||
# Container-wide env: every service + co-process daemon can
|
# Container-wide env, all from `agentEnv`: every unit and daemon
|
||||||
# resolve the agent's durable state dir without hard-coding it.
|
# (the bash-task runner where `hive-forge` and `git` run included)
|
||||||
# `environment.variables` only writes /etc/environment (login
|
# resolves the state dir and the forwarded service URLs without
|
||||||
# shells); `systemd.globalEnvironment` is the analogue for
|
# redeclaring them. `environment.variables` is what login shells
|
||||||
# systemd units so forge-avatar-sync /
|
# read; `systemd.globalEnvironment` is the analogue for units.
|
||||||
# hive-matrix-daemon etc. can read `$HYPERHIVE_STATE_DIR`
|
environment.variables = agentEnv;
|
||||||
# without each service having to redeclare it.
|
systemd.globalEnvironment = agentEnv;
|
||||||
environment.variables = {
|
systemd.services.${service}.environment = agentEnv // {
|
||||||
HIVE_LABEL = name;
|
|
||||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
};
|
|
||||||
systemd.globalEnvironment = {
|
|
||||||
HIVE_LABEL = name;
|
|
||||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
};
|
|
||||||
systemd.services.${service}.environment = {
|
|
||||||
HIVE_PORT = toString port;
|
HIVE_PORT = toString port;
|
||||||
HIVE_LABEL = name;
|
|
||||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
|
||||||
HYPERHIVE_HARNESS_DIR = "/agents/${name}/harness";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# Tells the harness the agent has no config repo of its own.
|
# Tells the harness the agent has no config repo of its own.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue