Skip to main content

aleph_types/message/execution/
volume.rs

1use crate::item_hash::ItemHash;
2use crate::memory_size::gigabyte_to_mebibyte;
3use crate::toolkit::serde::default_true;
4use memsizes::MiB;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8#[derive(thiserror::Error, Debug)]
9pub enum VolumeError {
10    #[error("value {size} is out of range ({min}..={max})")]
11    OutOfRange { size: u64, min: u64, max: u64 },
12}
13
14pub trait IsReadOnly {
15    fn is_read_only() -> bool;
16}
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct BaseVolume {
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub comment: Option<String>,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub mount: Option<PathBuf>,
24}
25
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct ImmutableVolume {
28    #[serde(flatten)]
29    pub base: BaseVolume,
30    #[serde(rename = "ref")]
31    pub reference: ItemHash,
32    #[serde(default = "default_true")]
33    pub use_latest: bool,
34}
35
36impl IsReadOnly for ImmutableVolume {
37    fn is_read_only() -> bool {
38        true
39    }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
43#[serde(try_from = "u64", into = "u64")]
44pub struct EphemeralVolumeSize(MiB);
45
46impl EphemeralVolumeSize {
47    const MIN: u64 = 1;
48    const MAX: u64 = 1000;
49}
50
51impl TryFrom<u64> for EphemeralVolumeSize {
52    type Error = VolumeError;
53
54    fn try_from(size: u64) -> Result<Self, Self::Error> {
55        if (Self::MIN..=Self::MAX).contains(&size) {
56            Ok(Self(MiB::from(size)))
57        } else {
58            Err(VolumeError::OutOfRange {
59                size,
60                min: Self::MIN,
61                max: Self::MAX,
62            })
63        }
64    }
65}
66
67impl From<EphemeralVolumeSize> for u64 {
68    fn from(size: EphemeralVolumeSize) -> Self {
69        size.0.count()
70    }
71}
72
73impl From<MiB> for EphemeralVolumeSize {
74    fn from(size: MiB) -> Self {
75        Self(size)
76    }
77}
78
79impl From<EphemeralVolumeSize> for MiB {
80    fn from(value: EphemeralVolumeSize) -> Self {
81        value.0
82    }
83}
84
85/// Ephemeral volume.
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87pub struct EphemeralVolume {
88    #[serde(flatten)]
89    pub base: BaseVolume,
90    ephemeral: bool,
91    pub size_mib: EphemeralVolumeSize,
92}
93
94impl EphemeralVolume {
95    /// Create a new ephemeral volume with the given size and mount point.
96    pub fn new(size_mib: u64, mount: impl Into<PathBuf>) -> Result<Self, VolumeError> {
97        Ok(Self {
98            base: BaseVolume {
99                comment: None,
100                mount: Some(mount.into()),
101            },
102            ephemeral: true,
103            size_mib: EphemeralVolumeSize::try_from(size_mib)?,
104        })
105    }
106}
107
108impl IsReadOnly for EphemeralVolume {
109    fn is_read_only() -> bool {
110        false
111    }
112}
113
114/// A reference volume to copy as a persistent volume.
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct ParentVolume {
117    #[serde(rename = "ref")]
118    pub reference: ItemHash,
119    #[serde(default = "default_true")]
120    pub use_latest: bool,
121}
122
123/// Where to persist the volume.
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125#[serde(rename_all = "lowercase")]
126pub enum VolumePersistence {
127    Host,
128    Store,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
132#[serde(try_from = "u64", into = "u64")]
133pub struct PersistentVolumeSize(MiB);
134
135impl PersistentVolumeSize {
136    const MIN: u64 = 1;
137    const MAX: u64 = gigabyte_to_mebibyte(2048);
138}
139
140impl TryFrom<u64> for PersistentVolumeSize {
141    type Error = VolumeError;
142
143    fn try_from(size: u64) -> Result<Self, Self::Error> {
144        if (Self::MIN..=Self::MAX).contains(&size) {
145            Ok(Self(MiB::from(size)))
146        } else {
147            Err(VolumeError::OutOfRange {
148                size,
149                min: Self::MIN,
150                max: Self::MAX,
151            })
152        }
153    }
154}
155
156impl From<PersistentVolumeSize> for u64 {
157    fn from(size: PersistentVolumeSize) -> Self {
158        size.0.count()
159    }
160}
161
162impl From<MiB> for PersistentVolumeSize {
163    fn from(size: MiB) -> Self {
164        Self(size)
165    }
166}
167
168impl From<PersistentVolumeSize> for MiB {
169    fn from(value: PersistentVolumeSize) -> Self {
170        value.0
171    }
172}
173
174#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
175pub struct PersistentVolume {
176    #[serde(flatten)]
177    pub base: BaseVolume,
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub parent: Option<ParentVolume>,
180    #[serde(default, skip_serializing_if = "Option::is_none")]
181    pub persistence: Option<VolumePersistence>,
182    #[serde(default, skip_serializing_if = "Option::is_none")]
183    pub name: Option<String>,
184    pub size_mib: PersistentVolumeSize,
185}
186
187impl IsReadOnly for PersistentVolume {
188    fn is_read_only() -> bool {
189        false
190    }
191}
192
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194#[serde(untagged)]
195pub enum MachineVolume {
196    Immutable(ImmutableVolume),
197    Ephemeral(EphemeralVolume),
198    Persistent(PersistentVolume),
199}
200
201/// Root file system of a VM instance.
202///
203/// The root file system of an instance is built as a copy of a reference image, named parent
204/// image. The user determines a custom size and persistence model.
205#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub struct RootfsVolume {
207    pub parent: ParentVolume,
208    pub persistence: VolumePersistence,
209    pub size_mib: PersistentVolumeSize,
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub forgotten_by: Option<Vec<ItemHash>>,
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    /// Sanity test for the read-only property on each volume type.
220    fn test_is_read_only() {
221        assert!(ImmutableVolume::is_read_only());
222        assert!(!EphemeralVolume::is_read_only());
223        assert!(!PersistentVolume::is_read_only());
224    }
225}