use super::audit_chain::{AdminAuditAppendError, AdminAuditChainAppender};
use super::failure_chain::{FailureAppendError, FailureChainAppender};
use super::ice::AdminAuditRecord;
use super::log_chain::{LogAppendError, LogChainAppender};
use super::logs::LogRecord;
use super::snapshot::FailureRecord;
use crate::adapter::net::redex::TypedRedexFile;
pub struct RedexAdminAuditAppender {
file: TypedRedexFile<AdminAuditRecord>,
}
impl RedexAdminAuditAppender {
pub fn new(file: crate::adapter::net::redex::RedexFile) -> Self {
Self {
file: TypedRedexFile::new(file),
}
}
}
impl AdminAuditChainAppender for RedexAdminAuditAppender {
fn append(&self, record: &AdminAuditRecord) -> Result<(), AdminAuditAppendError> {
self.file
.append(record)
.map_err(|e| AdminAuditAppendError {
reason: e.to_string(),
})?;
Ok(())
}
}
pub struct RedexLogAppender {
file: TypedRedexFile<LogRecord>,
}
impl RedexLogAppender {
pub fn new(file: crate::adapter::net::redex::RedexFile) -> Self {
Self {
file: TypedRedexFile::new(file),
}
}
}
impl LogChainAppender for RedexLogAppender {
fn append(&self, record: &LogRecord) -> Result<(), LogAppendError> {
self.file.append(record).map_err(|e| LogAppendError {
reason: e.to_string(),
})?;
Ok(())
}
}
pub struct RedexFailureAppender {
file: TypedRedexFile<FailureRecord>,
}
impl RedexFailureAppender {
pub fn new(file: crate::adapter::net::redex::RedexFile) -> Self {
Self {
file: TypedRedexFile::new(file),
}
}
}
impl FailureChainAppender for RedexFailureAppender {
fn append(&self, record: &FailureRecord) -> Result<(), FailureAppendError> {
self.file.append(record).map_err(|e| FailureAppendError {
reason: e.to_string(),
})?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::super::event::AdminEvent;
use super::super::ice::VerificationOutcome;
use super::super::logs::LogLevel;
use super::*;
use crate::adapter::net::channel::ChannelName;
use crate::adapter::net::redex::{Redex, RedexFile, RedexFileConfig};
use std::time::Duration;
fn fresh_redex_file(name: &str) -> RedexFile {
let redex = Redex::new();
let cn = ChannelName::new(name).expect("valid channel name");
redex
.open_file(&cn, RedexFileConfig::default())
.expect("open in-memory redex file")
}
#[test]
fn admin_audit_appender_writes_record_through_typed_file() {
let file = fresh_redex_file("admin-audit-test");
let reader = TypedRedexFile::<AdminAuditRecord>::new(file.clone());
let appender = RedexAdminAuditAppender::new(file);
let record = AdminAuditRecord {
seq: 1,
committed_at_ms: 1_700_000_000_000,
event: AdminEvent::FreezeCluster {
ttl: Duration::from_secs(30),
},
operator_ids: vec![7],
outcome: VerificationOutcome::Accepted,
chain_pending: false,
};
appender.append(&record).expect("append");
let read = reader.file().read_range(0, 1);
assert_eq!(read.len(), 1);
let decoded: AdminAuditRecord = postcard::from_bytes(&read[0].payload).expect("decode");
assert_eq!(decoded, record);
}
#[test]
fn log_appender_writes_record_through_typed_file() {
let file = fresh_redex_file("log-test");
let reader = TypedRedexFile::<LogRecord>::new(file.clone());
let appender = RedexLogAppender::new(file);
let record = LogRecord {
seq: 5,
ts_ms: 1_700_000_000_000,
level: LogLevel::Warn,
daemon_id: Some(7),
node_id: Some(100),
message: "throttling".into(),
chain_pending: false,
};
appender.append(&record).expect("append");
let read = reader.file().read_range(0, 1);
assert_eq!(read.len(), 1);
let decoded: LogRecord = postcard::from_bytes(&read[0].payload).expect("decode");
assert_eq!(decoded, record);
}
#[test]
fn failure_appender_writes_record_through_typed_file() {
let file = fresh_redex_file("failures-test");
let reader = TypedRedexFile::<FailureRecord>::new(file.clone());
let appender = RedexFailureAppender::new(file);
let record = FailureRecord {
seq: 2,
source: "daemon:telemetry".into(),
reason: "drain timeout".into(),
recorded_at_ms: 1_700_000_000_000,
};
appender.append(&record).expect("append");
let read = reader.file().read_range(0, 1);
assert_eq!(read.len(), 1);
let decoded: FailureRecord = postcard::from_bytes(&read[0].payload).expect("decode");
assert_eq!(decoded, record);
}
}