mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-07-03 02:51:04 +02:00
rename compiler NanoError and fix cluster config warnings
This commit is contained in:
parent
5243c048aa
commit
4590c91f9d
17 changed files with 499 additions and 333 deletions
|
|
@ -160,7 +160,7 @@ pub async fn plan_config_dir(config_dir: impl AsRef<Path>) -> PlanOutput {
|
|||
|
||||
// Plan is read-only: pending sidecars are reported, never acted on
|
||||
// (RFC-004 open question 3 keeps read-only commands warn-only).
|
||||
warn_pending_recovery_sidecars(&desired.config_dir, &mut diagnostics);
|
||||
warn_pending_recovery_sidecars(&backend, &mut diagnostics).await;
|
||||
|
||||
let mut prior_resources = BTreeMap::new();
|
||||
let mut prior_state: Option<ClusterState> = None;
|
||||
|
|
@ -1260,7 +1260,7 @@ pub async fn status_config_dir(config_dir: impl AsRef<Path>) -> StatusOutput {
|
|||
backend
|
||||
.observe_lock(&mut observations, &mut diagnostics)
|
||||
.await;
|
||||
warn_pending_recovery_sidecars(&parsed.config_dir, &mut diagnostics);
|
||||
warn_pending_recovery_sidecars(&backend, &mut diagnostics).await;
|
||||
|
||||
let mut resource_digests = BTreeMap::new();
|
||||
let mut resource_statuses = BTreeMap::new();
|
||||
|
|
|
|||
|
|
@ -321,6 +321,32 @@ impl ClusterStore {
|
|||
|
||||
// ---- recovery sidecars ----
|
||||
|
||||
pub(crate) async fn list_recovery_sidecar_locations(
|
||||
&self,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) -> Vec<String> {
|
||||
let dir_uri = self.uri(CLUSTER_RECOVERIES_DIR);
|
||||
let mut uris = match self.adapter.list_dir(&dir_uri).await {
|
||||
Ok(uris) => uris,
|
||||
Err(err) => {
|
||||
diagnostics.push(Diagnostic::warning(
|
||||
"recovery_sidecar_read_error",
|
||||
CLUSTER_RECOVERIES_DIR,
|
||||
format!("could not list '{CLUSTER_RECOVERIES_DIR}': {err}"),
|
||||
));
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
uris.retain(|uri| uri.ends_with(".json"));
|
||||
uris.sort();
|
||||
uris.into_iter()
|
||||
.map(|uri| match uri.rsplit('/').next() {
|
||||
Some(name) => format!("{}/{name}", self.display(CLUSTER_RECOVERIES_DIR)),
|
||||
None => uri,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) async fn list_recovery_sidecars(
|
||||
&self,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
|
|
|
|||
|
|
@ -427,21 +427,14 @@ pub(crate) async fn mark_approvals_consumed(backend: &ClusterStore, approval_ids
|
|||
}
|
||||
|
||||
/// Read-only commands report pending sidecars without acting on them.
|
||||
pub(crate) fn warn_pending_recovery_sidecars(config_dir: &Path, diagnostics: &mut Vec<Diagnostic>) {
|
||||
let recoveries_dir = config_dir.join(CLUSTER_RECOVERIES_DIR);
|
||||
let Ok(entries) = fs::read_dir(&recoveries_dir) else {
|
||||
return;
|
||||
};
|
||||
let mut names: Vec<String> = entries
|
||||
.flatten()
|
||||
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "json"))
|
||||
.map(|entry| entry.file_name().to_string_lossy().into_owned())
|
||||
.collect();
|
||||
names.sort();
|
||||
for name in names {
|
||||
pub(crate) async fn warn_pending_recovery_sidecars(
|
||||
backend: &ClusterStore,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
for location in backend.list_recovery_sidecar_locations(diagnostics).await {
|
||||
diagnostics.push(Diagnostic::warning(
|
||||
"cluster_recovery_pending",
|
||||
format!("{CLUSTER_RECOVERIES_DIR}/{name}"),
|
||||
location,
|
||||
"a recovery sidecar from an interrupted apply is pending; the next state-mutating command will classify it",
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3375,6 +3375,67 @@ policies:
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_only_commands_warn_on_pending_recovery_sidecar_in_storage_root() {
|
||||
let dir = fixture();
|
||||
let storage = tempfile::tempdir().unwrap();
|
||||
let storage_path = storage.path().to_string_lossy().to_string();
|
||||
let mut config = fs::read_to_string(dir.path().join(CLUSTER_CONFIG_FILE)).unwrap();
|
||||
config = config.replace(
|
||||
"version: 1\n",
|
||||
&format!("version: 1\nstorage: {storage_path}\n"),
|
||||
);
|
||||
fs::write(dir.path().join(CLUSTER_CONFIG_FILE), config).unwrap();
|
||||
|
||||
let desired = validate_config_dir(dir.path());
|
||||
assert!(desired.ok, "{:?}", desired.diagnostics);
|
||||
let schema_digest = desired
|
||||
.resource_digests
|
||||
.get("schema.knowledge")
|
||||
.unwrap()
|
||||
.clone();
|
||||
let graph_composite = graph_digest(
|
||||
"knowledge",
|
||||
Some(&schema_digest),
|
||||
Some(&BTreeMap::new()),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
write_state_resources(
|
||||
storage.path(),
|
||||
&[
|
||||
("graph.knowledge", graph_composite.as_str()),
|
||||
("schema.knowledge", schema_digest.as_str()),
|
||||
],
|
||||
);
|
||||
write_create_sidecar(storage.path(), "knowledge", "irrelevant", "01STORAGE");
|
||||
|
||||
let status = status_config_dir(dir.path()).await;
|
||||
assert!(status.ok, "{:?}", status.diagnostics);
|
||||
assert!(
|
||||
status
|
||||
.diagnostics
|
||||
.iter()
|
||||
.any(|diagnostic| diagnostic.code == "cluster_recovery_pending"
|
||||
&& diagnostic.path.contains("01STORAGE.json")),
|
||||
"{:?}",
|
||||
status.diagnostics
|
||||
);
|
||||
|
||||
let plan = plan_config_dir(dir.path()).await;
|
||||
assert!(plan.ok, "{:?}", plan.diagnostics);
|
||||
assert!(
|
||||
plan.diagnostics
|
||||
.iter()
|
||||
.any(|diagnostic| diagnostic.code == "cluster_recovery_pending"
|
||||
&& diagnostic.path.contains("01STORAGE.json")),
|
||||
"{:?}",
|
||||
plan.diagnostics
|
||||
);
|
||||
|
||||
assert!(!dir.path().join(CLUSTER_RECOVERIES_DIR).exists());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_annotates_apply_dispositions() {
|
||||
let dir = fixture();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue