push ci run metrics to victoriametrics, with a grafana board
A swarm-ci-metrics host unit on a 5-minute timer lists every forge repo's Actions runs as the new read-only ci-metrics account and pushes each finished run's duration and queue wait over OTLP, stamped at the run's stop time. The first pass backfills from run 1. swarm-controller mints the account's token into bao and keeps a ci-metrics team with read on the Actions unit in every organisation. Closes #5376
This commit is contained in:
parent
8b7a4e7955
commit
b072fbf4d6
36 changed files with 2849 additions and 56 deletions
|
|
@ -163,6 +163,11 @@ enum SwarmNodeKind {
|
|||
/// the `hives` team, and holds a live token in the swarm store, where the
|
||||
/// builder reads it to fetch config repos. See `forge::builder_account`.
|
||||
MintBuilderForgeToken,
|
||||
/// Make sure the CI metrics poller's read-only forge account exists, is in
|
||||
/// its team in every organisation, and holds a live token in the swarm
|
||||
/// store, where the poller reads it to list CI runs. See
|
||||
/// `forge::ci_metrics_account`.
|
||||
MintCiMetricsForgeToken,
|
||||
/// Build `agent`'s config at `rev`, the head of its config PR `pr`, on
|
||||
/// the swarm builder, and write the result as a commit status on `rev`.
|
||||
/// See `builder`.
|
||||
|
|
@ -212,6 +217,7 @@ impl hive_jobq_wire::WireNode for SwarmNodeKind {
|
|||
SwarmNodeKind::MintHiveForgeToken { .. } => "mint_hive_forge_token".to_owned(),
|
||||
SwarmNodeKind::MintClassifierForgeToken => "mint_classifier_forge_token".to_owned(),
|
||||
SwarmNodeKind::MintBuilderForgeToken => "mint_builder_forge_token".to_owned(),
|
||||
SwarmNodeKind::MintCiMetricsForgeToken => "mint_ci_metrics_forge_token".to_owned(),
|
||||
SwarmNodeKind::BuildAgent { .. } => "build_agent".to_owned(),
|
||||
SwarmNodeKind::RenewAgentQueueCredential { .. } => {
|
||||
"renew_agent_queue_credential".to_owned()
|
||||
|
|
@ -243,9 +249,9 @@ impl hive_jobq_wire::WireNode for SwarmNodeKind {
|
|||
}
|
||||
SwarmNodeKind::MintHiveSenderToken { hive }
|
||||
| SwarmNodeKind::MintHiveForgeToken { hive } => serde_json::json!({ "hive": hive }),
|
||||
SwarmNodeKind::MintClassifierForgeToken | SwarmNodeKind::MintBuilderForgeToken => {
|
||||
serde_json::json!({})
|
||||
}
|
||||
SwarmNodeKind::MintClassifierForgeToken
|
||||
| SwarmNodeKind::MintBuilderForgeToken
|
||||
| SwarmNodeKind::MintCiMetricsForgeToken => serde_json::json!({}),
|
||||
SwarmNodeKind::BuildAgent { agent, rev, pr } => {
|
||||
serde_json::json!({ "agent": agent, "rev": rev, "pr": pr })
|
||||
}
|
||||
|
|
@ -413,6 +419,7 @@ async fn run_swarm_node(
|
|||
SwarmNodeKind::MintHiveForgeToken { hive } => mint_hive_forge_token(forge, &hive).await,
|
||||
SwarmNodeKind::MintClassifierForgeToken => mint_classifier_forge_token(forge).await,
|
||||
SwarmNodeKind::MintBuilderForgeToken => mint_builder_forge_token(forge).await,
|
||||
SwarmNodeKind::MintCiMetricsForgeToken => mint_ci_metrics_forge_token(forge).await,
|
||||
SwarmNodeKind::BuildAgent { agent, rev, pr } => {
|
||||
build_agent(&deps, forge, agent, rev, pr).await
|
||||
}
|
||||
|
|
@ -606,6 +613,30 @@ async fn mint_builder_forge_token(
|
|||
}
|
||||
}
|
||||
|
||||
/// The `MintCiMetricsForgeToken` arm, lifted out for the same reason.
|
||||
async fn mint_ci_metrics_forge_token(
|
||||
forge: Option<Arc<forge::Client>>,
|
||||
) -> hive_jobq::scheduler::Outcome {
|
||||
use hive_jobq::scheduler::Outcome;
|
||||
|
||||
let Some(client) = forge else {
|
||||
return Outcome::Failed(
|
||||
"no forge access here (SWARM_CONTROLLER_FORGE_URL unset, or its token \
|
||||
not in the swarm secret store), so no ci metrics forge token can be minted"
|
||||
.to_owned(),
|
||||
);
|
||||
};
|
||||
match forge_node(
|
||||
"mint_ci_metrics_forge_token",
|
||||
client.ensure_ci_metrics_forge_token(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => Outcome::Done,
|
||||
Err(e) => Outcome::Failed(format!("{e:#}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// The `MintClassifierForgeToken` arm, lifted out for the same reason.
|
||||
async fn mint_classifier_forge_token(
|
||||
forge: Option<Arc<forge::Client>>,
|
||||
|
|
@ -2760,6 +2791,20 @@ fn queue_builder_forge_token_mint(
|
|||
.map_err(|e| anyhow::anyhow!("{e}"))
|
||||
}
|
||||
|
||||
/// Insert one `MintCiMetricsForgeToken` job and return its node's id.
|
||||
/// `forge::ci_metrics_account::spawn`'s periodic pass is the one caller.
|
||||
fn queue_ci_metrics_forge_token_mint(
|
||||
sched: &Mutex<hive_jobq::scheduler::Scheduler<SwarmNodeKind, SwarmResourceKind>>,
|
||||
) -> Result<Vec<hive_jobq::NodeId>> {
|
||||
sched
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.insert_job(None, |b| {
|
||||
vec![b.node(SwarmNodeKind::MintCiMetricsForgeToken).guid()]
|
||||
})
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))
|
||||
}
|
||||
|
||||
/// The homeserver agents' own matrix accounts are minted on. Unset is this
|
||||
/// controller creating agents with no matrix account, which the mint node
|
||||
/// reports by name rather than `main` refusing to start.
|
||||
|
|
@ -2835,6 +2880,12 @@ fn spawn_forge_workers(
|
|||
}
|
||||
});
|
||||
let sched = Arc::clone(jobq);
|
||||
forge::ci_metrics_account::spawn(Arc::clone(&client), move || {
|
||||
if let Err(e) = queue_ci_metrics_forge_token_mint(&sched) {
|
||||
tracing::warn!(error = %format!("{e:#}"), "ci metrics forge token: queueing failed");
|
||||
}
|
||||
});
|
||||
let sched = Arc::clone(jobq);
|
||||
forge::docs_grant::spawn(Arc::clone(&client), move |agents| {
|
||||
if let Err(e) = queue_docs_grants(&sched, agents) {
|
||||
tracing::warn!(error = %format!("{e:#}"), "docs grants: queueing failed");
|
||||
|
|
@ -4725,6 +4776,7 @@ mod tests {
|
|||
for name in [
|
||||
crate::forge::builder_account::LOGIN,
|
||||
crate::forge::classifier_account::LOGIN,
|
||||
crate::forge::ci_metrics_account::LOGIN,
|
||||
] {
|
||||
let mut warnings = Vec::new();
|
||||
let broken = super::broken_name_rules(name, Some(&raw), &mut warnings);
|
||||
|
|
@ -4959,6 +5011,10 @@ mod tests {
|
|||
"mint_builder_forge_token",
|
||||
SwarmNodeKind::MintBuilderForgeToken,
|
||||
),
|
||||
(
|
||||
"mint_ci_metrics_forge_token",
|
||||
SwarmNodeKind::MintCiMetricsForgeToken,
|
||||
),
|
||||
];
|
||||
for (label, kind) in nodes {
|
||||
let mut sched = hive_jobq::scheduler::Scheduler::new(
|
||||
|
|
|
|||
Loading…
Reference in a new issue