Skip to main content

cc_lb_plugin_conformance/
self_check.rs

1use cc_lb_plugin_wire::self_check::{
2    SelfCheckStage as WireSelfCheckStage, SelfCheckStatus as WireSelfCheckStatus,
3};
4use cc_lb_runtime_protocol::self_check::{SelfCheckExecutionError, execute_self_check};
5use thiserror::Error;
6
7pub fn run(wasm: &[u8]) -> Result<SelfCheckReport, SelfCheckError> {
8    let response = execute_self_check(wasm).map_err(SelfCheckError::from_protocol)?;
9
10    Ok(SelfCheckReport {
11        status: response.status.into(),
12        failures: response
13            .failures
14            .into_iter()
15            .map(|failure| SelfCheckFailure {
16                function: stage_name(failure.stage).to_owned(),
17                version: 0,
18                reason: failure.message,
19            })
20            .collect(),
21    })
22}
23
24#[non_exhaustive]
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct SelfCheckReport {
27    pub status: SelfCheckStatus,
28    pub failures: Vec<SelfCheckFailure>,
29}
30
31#[non_exhaustive]
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum SelfCheckStatus {
34    Success,
35    Failure,
36}
37
38#[non_exhaustive]
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct SelfCheckFailure {
41    pub function: String,
42    pub version: u32,
43    pub reason: String,
44}
45
46#[non_exhaustive]
47#[derive(Debug, Clone, Error, PartialEq, Eq)]
48pub enum SelfCheckError {
49    #[error("plugin instantiation failed: {reason}")]
50    Instantiate { reason: String },
51    #[error("plugin does not export cc_lb_self_check")]
52    MissingExport,
53    #[error("self-check reported failure status")]
54    FailureStatus,
55    #[error("self-check output invalid: {reason}")]
56    Output { reason: String },
57    #[error("wasm execution failed: {reason}")]
58    WasmTrap { reason: String },
59}
60
61impl SelfCheckError {
62    pub(crate) fn from_protocol(error: SelfCheckExecutionError) -> Self {
63        let reason = error.to_string();
64        match error {
65            SelfCheckExecutionError::Instantiate { reason } => {
66                SelfCheckError::Instantiate { reason }
67            }
68            SelfCheckExecutionError::MissingSelfCheckExport => SelfCheckError::MissingExport,
69            SelfCheckExecutionError::Call { reason } => SelfCheckError::WasmTrap { reason },
70            SelfCheckExecutionError::FailureStatus { .. } => SelfCheckError::FailureStatus,
71            SelfCheckExecutionError::Validation(_)
72            | SelfCheckExecutionError::SerializeRequest { .. }
73            | SelfCheckExecutionError::OutputTooLarge { .. }
74            | SelfCheckExecutionError::DecodeResponse { .. }
75            | SelfCheckExecutionError::SuccessWithFailures { .. }
76            | SelfCheckExecutionError::FailureWithoutFailures
77            | SelfCheckExecutionError::Clock { .. } => SelfCheckError::Output { reason },
78            _ => unreachable!(),
79        }
80    }
81}
82
83impl From<WireSelfCheckStatus> for SelfCheckStatus {
84    fn from(status: WireSelfCheckStatus) -> Self {
85        match status {
86            WireSelfCheckStatus::Success => SelfCheckStatus::Success,
87            WireSelfCheckStatus::Failure => SelfCheckStatus::Failure,
88        }
89    }
90}
91
92fn stage_name(stage: WireSelfCheckStage) -> &'static str {
93    match stage {
94        WireSelfCheckStage::RequestPreparation => "request_preparation",
95        WireSelfCheckStage::WireFunctionTest => "wire_function_test",
96        WireSelfCheckStage::CapabilityVerification => "capability_verification",
97        WireSelfCheckStage::ResponseValidation => "response_validation",
98    }
99}