dashboard: remove the L0GS page and its journal backend
Logs live at swarm level now (VictoriaLogs, read through Grafana
Explore). The hive dashboard's own journal viewer goes, with
everything that existed only to serve it:
- frontend: /logs.html, logs.js, logs.css, the H0M3 "Logs" tile and
the per-agent menu's "journal logs ->" link. The build-log list
rules BU1LDS used from logs.css move into builds.css.
- hive-c0re: GET /api/journal/{name} and GET /api/journal-host, and
priv_client::read_container_journal.
- hive-priv / hive-priv-sock: the ReadContainerJournal request, its
JournalQuery/JournalOutput wire types and the journalctl -M executor.
- nix: hive-c0re's systemd-journal supplementary group, whose only
reader was the journal-host handler.
- docs: the L0GS section, the API entry and the security table row.
Closes #5113
Closes #3819
This commit is contained in:
parent
71e7fd8b20
commit
884fb3e08b
31 changed files with 192 additions and 1231 deletions
|
|
@ -23,9 +23,9 @@ use std::path::{Path, PathBuf};
|
|||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use hive_priv_sock::{
|
||||
AGENT_PREFIX, AGENT_RUNTIME_ROOT, AGENT_STATE_ROOT, BindMount, CredentialMount, InfraAction,
|
||||
InfraContainer, JournalQuery, MEASURE_AGENT_DISK_TIMEOUT, META_DIR, MIGRATE_STAGING_ROOT,
|
||||
NetworkIsolation, PAUSED_MARKER_FILE, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse,
|
||||
PrivStream, PrivStreamLine, SIBLING_CONTAINERS,
|
||||
InfraContainer, MEASURE_AGENT_DISK_TIMEOUT, META_DIR, MIGRATE_STAGING_ROOT, NetworkIsolation,
|
||||
PAUSED_MARKER_FILE, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse, PrivStream,
|
||||
PrivStreamLine, SIBLING_CONTAINERS,
|
||||
};
|
||||
use tokio::io::{AsyncWriteExt, BufReader};
|
||||
use tokio::net::unix::OwnedWriteHalf;
|
||||
|
|
@ -372,11 +372,6 @@ async fn exec(
|
|||
|
||||
PrivRequest::ListContainers => container_run(&["list"]).await,
|
||||
|
||||
PrivRequest::ReadContainerJournal {
|
||||
ref container,
|
||||
ref query,
|
||||
} => exec_read_container_journal(container, query).await,
|
||||
|
||||
PrivRequest::WriteNspawnFlags {
|
||||
ref container,
|
||||
ref binds,
|
||||
|
|
@ -535,15 +530,6 @@ async fn destroy_container(name: &str) -> Result<(String, String)> {
|
|||
container_run(&["destroy", &container_system_name(name)]).await
|
||||
}
|
||||
|
||||
/// `ReadContainerJournal`.
|
||||
async fn exec_read_container_journal(
|
||||
container: &str,
|
||||
query: &JournalQuery,
|
||||
) -> Result<(String, String)> {
|
||||
validate_container_system_name(container)?;
|
||||
read_container_journal(container, query).await
|
||||
}
|
||||
|
||||
/// `SetAgentPaused`.
|
||||
fn exec_set_agent_paused(agent_name: &str, paused: bool) -> Result<(String, String)> {
|
||||
validate_agent_name(agent_name)?;
|
||||
|
|
@ -2695,60 +2681,6 @@ async fn container_run_streaming(
|
|||
Ok((String::new(), String::new()))
|
||||
}
|
||||
|
||||
/// Read a container's journal as root via `journalctl -M`. Returns
|
||||
/// `(stdout, stderr)`. Unlike `container_run` a non-zero exit is *not* a
|
||||
/// hard error — journalctl's own diagnostic (folded into `stderr` with
|
||||
/// the exit status) is what the caller surfaces to the operator, so the
|
||||
/// helper never bails.
|
||||
async fn read_container_journal(container: &str, query: &JournalQuery) -> Result<(String, String)> {
|
||||
let mut args: Vec<String> = vec![
|
||||
"-M".to_owned(),
|
||||
container.to_owned(),
|
||||
"--no-pager".to_owned(),
|
||||
format!("--output={}", query.output.as_journalctl()),
|
||||
"-n".to_owned(),
|
||||
query.lines.to_string(),
|
||||
];
|
||||
if query.boot {
|
||||
args.push("-b".to_owned());
|
||||
}
|
||||
if let Some(u) = &query.unit {
|
||||
args.push("-u".to_owned());
|
||||
args.push(u.clone());
|
||||
}
|
||||
if let Some(p) = &query.priority {
|
||||
args.push("-p".to_owned());
|
||||
args.push(p.clone());
|
||||
}
|
||||
// `--grep=`/`--since=`/`--until=` use the `=`-joined form so a value
|
||||
// can never be parsed as a separate journalctl flag.
|
||||
if let Some(g) = &query.grep {
|
||||
args.push(format!("--grep={g}"));
|
||||
}
|
||||
if let Some(s) = &query.since {
|
||||
args.push(format!("--since={s}"));
|
||||
}
|
||||
if let Some(u) = &query.until {
|
||||
args.push(format!("--until={u}"));
|
||||
}
|
||||
let out = Command::new("journalctl")
|
||||
.args(&args)
|
||||
.output()
|
||||
.await
|
||||
.context("invoke journalctl -M")?;
|
||||
let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
|
||||
let stderr = if out.status.success() {
|
||||
String::from_utf8_lossy(&out.stderr).into_owned()
|
||||
} else {
|
||||
format!(
|
||||
"journalctl -M {container} exited {}: {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr).trim()
|
||||
)
|
||||
};
|
||||
Ok((stdout, stderr))
|
||||
}
|
||||
|
||||
/// Synchronise the host's nginx unit after an `agents.conf` write.
|
||||
///
|
||||
/// Queries `ActiveState` and dispatches:
|
||||
|
|
|
|||
Loading…
Reference in a new issue