Skip to main content

cc_lb_plugin_conformance/
self_check.rs

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