summaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/api')
-rw-r--r--src/api/mod.rs7
-rw-r--r--src/api/peers.rs12
-rw-r--r--src/api/stats.rs34
-rw-r--r--src/api/stats/prometheus.rs49
4 files changed, 95 insertions, 7 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 1fba48b..affb9ef 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -120,8 +120,11 @@ fn parse_write_path(
if path.first().map(String::as_str) != Some(crate::remake::snapshot::META_ROOT) {
return Ok(path);
}
- let epochs = path.len() < 2 || path[1] == crate::remake::snapshot::EPOCHS;
- if epochs || !state.protocol_writers.contains(&user.0) {
+ // `_continuum` itself, epoch metadata and the swarm ID: nobody.
+ let fixed = path.len() < 2
+ || path[1] == crate::remake::snapshot::EPOCHS
+ || path[1] == crate::remake::snapshot::SWARM;
+ if fixed || !state.protocol_writers.contains(&user.0) {
warn!(user = %user.0, method, path = %key, "Refused a write to protocol data");
return Err(StatusCode::FORBIDDEN);
}
diff --git a/src/api/peers.rs b/src/api/peers.rs
index 919f187..dc4bd3b 100644
--- a/src/api/peers.rs
+++ b/src/api/peers.rs
@@ -100,8 +100,8 @@ pub(crate) struct PeerListResponse {
#[derive(Debug, Deserialize)]
pub(crate) struct DeletePeerQuery {
- #[serde(default)]
- force: bool,
+ /// The checks to override (`force`): `alive`.
+ force: Option<String>,
}
#[utoipa::path(
@@ -197,12 +197,12 @@ pub(crate) async fn list_peers(
tag = "Peers",
params(
("peer" = String, Path, description = "Peer public key"),
- ("force" = Option<bool>, Query, description = "Also forget a peer that is currently alive; it returns on its next message")
+ ("force" = Option<String>, Query, description = "`true` or `alive`: also forget a peer that is currently alive; it returns on its next message")
),
responses(
(status = 204, description = "Swarm-wide forget recorded"),
(status = 400, description = "Not a public key, or the local node's own key"),
- (status = 409, description = "Peer is alive; pass force=true"),
+ (status = 409, description = "Peer is alive; pass force=alive"),
(status = 503, description = "Node is not ready")
)
)]
@@ -217,6 +217,8 @@ pub(crate) async fn delete_peer(
if peer.is_empty() || peer == state.local_id() {
return Err(StatusCode::BAD_REQUEST);
}
+ let force = crate::force::Force::parse(query.force.as_deref(), &["alive"])
+ .map_err(|_| StatusCode::BAD_REQUEST)?;
// Unknown locally is fine: other nodes may still know the peer.
if let Some(peer_state_mutex) = state.get_peer_state(&peer).await {
@@ -224,7 +226,7 @@ pub(crate) async fn delete_peer(
.lock()
.await
.is_alive(&state.timing, Instant::now())
- && !query.force
+ && !force.allows("alive")
{
return Err(StatusCode::CONFLICT);
}
diff --git a/src/api/stats.rs b/src/api/stats.rs
index 838cc0f..fa11b45 100644
--- a/src/api/stats.rs
+++ b/src/api/stats.rs
@@ -9,6 +9,8 @@ use axum::{
http::{HeaderMap, header},
response::{IntoResponse, Response},
};
+use std::collections::BTreeMap;
+
use serde::Serialize;
use super::peers::EpochInfo;
@@ -35,6 +37,21 @@ pub(crate) struct StatsResponse {
/// The peers listed in /peers together (a forgotten peer's are gone).
incidents: Incidents,
propagation: PropagationStats,
+ /// This node's own files (`files`), by kind.
+ disk: BTreeMap<&'static str, DiskStats>,
+}
+
+/// Reads and writes of one kind of file, since the node started.
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct DiskStats {
+ reads: u64,
+ read_bytes: u64,
+ writes: u64,
+ written_bytes: u64,
+ appends: u64,
+ appended_bytes: u64,
+ errors: u64,
}
/// How long changes written elsewhere took from their commit there until applied here,
@@ -195,5 +212,22 @@ async fn collect(state: &AppState) -> StatsResponse {
.map(|(le_ms, count)| PropagationBucket { le_ms, count })
.collect(),
},
+ disk: state
+ .files
+ .counts()
+ .into_iter()
+ .map(|(kind, counts)| {
+ let stats = DiskStats {
+ reads: counts.reads,
+ read_bytes: counts.read_bytes,
+ writes: counts.writes,
+ written_bytes: counts.written_bytes,
+ appends: counts.appends,
+ appended_bytes: counts.appended_bytes,
+ errors: counts.errors,
+ };
+ (kind.as_str(), stats)
+ })
+ .collect(),
}
}
diff --git a/src/api/stats/prometheus.rs b/src/api/stats/prometheus.rs
index f68ca85..74c18e9 100644
--- a/src/api/stats/prometheus.rs
+++ b/src/api/stats/prometheus.rs
@@ -54,6 +54,45 @@ impl Out {
}
}
+const DISK_COUNTERS: usize = 7;
+
+/// Name, help and value of each counter of one kind of file.
+fn disk_counters(c: &super::DiskStats) -> [(&'static str, &'static str, u64); DISK_COUNTERS] {
+ [
+ (
+ "disk_reads_total",
+ "Reads of this node's own files.",
+ c.reads,
+ ),
+ (
+ "disk_read_bytes_total",
+ "Bytes read from them.",
+ c.read_bytes,
+ ),
+ ("disk_writes_total", "Whole files written.", c.writes),
+ (
+ "disk_written_bytes_total",
+ "Bytes of them.",
+ c.written_bytes,
+ ),
+ (
+ "disk_appends_total",
+ "Appends to this node's own files.",
+ c.appends,
+ ),
+ (
+ "disk_appended_bytes_total",
+ "Bytes appended.",
+ c.appended_bytes,
+ ),
+ (
+ "disk_errors_total",
+ "Failed reads, writes and appends.",
+ c.errors,
+ ),
+ ]
+}
+
fn escape(value: &str) -> String {
value
.replace('\\', "\\\\")
@@ -297,6 +336,16 @@ pub(super) async fn render(state: &AppState, stats: &StatsResponse) -> String {
stats.stale_hello_replies as f64,
);
+ for counter in 0..DISK_COUNTERS {
+ for (i, (kind, counts)) in stats.disk.iter().enumerate() {
+ let (name, help, value) = disk_counters(counts)[counter];
+ if i == 0 {
+ out.family(name, "counter", help);
+ }
+ out.sample(name, &[("file", *kind)], value as f64);
+ }
+ }
+
let propagation = &stats.propagation;
out.family(
"propagation_delay_seconds",