Skip to main content

aleph_types/message/execution/
volume.rs

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