use std::sync::Arc;
use super::{Daemon, secrets, workspaces};
use crate::error::Result;
use crate::server::Registry;
pub(super) fn register(r: &mut Registry, d: &Arc<Daemon>) -> Result<()> {
let ctl = d.ctl.clone();
let bindings: secrets::Bindings = Arc::new(move |org: &crate::org::OrgId| {
let mut out = Vec::new();
for def in ctl.definitions().iter().filter(|def| def.org == *org) {
let used = crate::stack::secrets::used_keys(&def.file);
for (_, b) in def.secrets.iter().filter(|(k, _)| used.contains(*k)) {
out.push(secrets::Binding {
name: b.name.clone(),
driver: b.driver.clone(),
version: b.version,
stack: def.name.clone(),
});
}
}
out
});
let ctl = d.ctl.clone();
let in_use: secrets::InUse = Arc::new(move |org: &crate::org::OrgId, name: &str| {
ctl.definitions()
.iter()
.filter(|def| def.org == *org && def.store_secrets().contains(name))
.map(|def| def.name.clone())
.collect()
});
let ctl = d.ctl.clone();
let prepare: secrets::Prepare =
Arc::new(move |org: &crate::org::OrgId, name: &str, value: &[u8]| {
ctl.rotate_before_set(org, name, value)
});
let (ctl, wsm) = (d.ctl.clone(), d.workspaces.clone());
d.apps
.on_secret_changed(Arc::new(move |org: &crate::org::OrgId, name: &str| {
workspace_secret_changed(&ctl, &wsm, org, name, true);
}));
let (ctl, wsm, apps) = (d.ctl.clone(), d.workspaces.clone(), d.apps.clone());
let changed: secrets::Changed = Arc::new(move |org: &crate::org::OrgId, name: &str| {
let mut cycles = ctl.secret_changed(org, name);
let mut skipped = workspace_secret_changed(&ctl, &wsm, org, name, true);
match apps.database_password_changed(org, name) {
Ok(urls) => {
for url in urls {
cycles.extend(ctl.secret_changed(org, &url));
skipped.extend(workspace_secret_changed(&ctl, &wsm, org, &url, true));
}
}
Err(e) => ctl.note(
"error",
&crate::stack::qualified(org, "@secrets"),
format!("secret {name}: the database URL secret was not updated: {e}"),
),
}
secrets::Outcome { cycles, skipped }
});
let (ctl, wsm, store) = (d.ctl.clone(), d.workspaces.clone(), d.secrets.clone());
let refresh: secrets::Refresh = Arc::new(move |org: &crate::org::OrgId, name: &str| {
let (mut found, cycles) = ctl.refresh_secret(org, name)?;
if found.is_empty() && name.contains('/') && wsm.uses(org, name) {
let m = store.refresh(org, name)?;
found.push((m.driver, m.version));
}
let skipped = workspace_secret_changed(&ctl, &wsm, org, name, false);
Ok((found, secrets::Outcome { cycles, skipped }))
});
secrets::register(
r,
d.secrets.clone(),
secrets::Hooks {
prepare,
in_use,
changed,
refresh,
bindings,
},
)?;
Ok(())
}
fn workspace_secret_changed(
ctl: &crate::stack::Controller,
wsm: &workspaces::Workspaces,
org: &crate::org::OrgId,
name: &str,
event: bool,
) -> Vec<secrets::Skipped> {
let stack = crate::stack::qualified(org, "@workspaces");
wsm.secret_changed(org, name)
.into_iter()
.map(|(w, reason)| {
if event {
ctl.event(
"secret.rotated",
"warn",
&stack,
&w,
format!("new value of secret {name}: workspace {w} {reason}"),
);
}
secrets::Skipped {
kind: "workspace".into(),
name: w,
reason,
}
})
.collect()
}