kernel/config/
container.rs1use std::num::{NonZeroU32, NonZeroU64};
10
11use serde::{Deserialize, Serialize};
12
13use crate::{AxCode, AxError};
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(try_from = "String", into = "String")]
19pub struct ContainerImage(String);
20
21impl ContainerImage {
22 pub(crate) const PREFIX: &str = "sha256:";
23 pub(crate) const DIGEST_LENGTH: usize = 64;
24 pub(crate) const HEX_DIGITS: &str = "0123456789abcdef";
25 pub fn parse(image: &str) -> Result<Self, AxError> {
30 if image.strip_prefix(Self::PREFIX).is_some_and(|digest| {
31 digest.len() == Self::DIGEST_LENGTH
32 && digest
33 .bytes()
34 .all(|byte| Self::HEX_DIGITS.as_bytes().contains(&byte))
35 }) {
36 return Ok(Self(image.to_owned()));
37 }
38 Err(AxError::failure(
39 AxCode::ConfigInvalid,
40 "read the container image",
41 "the image is not a full local sha256 image ID",
42 )
43 .with_recovery(
44 "prepare an image yourself and use its sha256 ID from image inspect; exec never pulls an image",
45 ))
46 }
47
48 #[must_use]
49 pub fn as_str(&self) -> &str {
50 &self.0
51 }
52}
53
54impl TryFrom<String> for ContainerImage {
55 type Error = AxError;
56
57 fn try_from(image: String) -> Result<Self, Self::Error> {
58 Self::parse(&image)
59 }
60}
61
62impl From<ContainerImage> for String {
63 fn from(image: ContainerImage) -> Self {
64 image.0
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
72#[serde(deny_unknown_fields)]
73pub struct ContainerLimits {
74 pub image: ContainerImage,
75 pub user: NonZeroU32,
76 pub cpu_millis: NonZeroU32,
77 pub memory_bytes: NonZeroU64,
78 pub pids: NonZeroU32,
79}
80
81#[cfg(test)]
82#[allow(clippy::unwrap_used, reason = "test code")]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn configuration_and_serde_share_the_immutable_image_admission() {
88 for image in [
89 "",
90 "alpine:latest",
91 "--privileged",
92 "sha256:abc",
93 "sha256:fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffF",
94 ] {
95 assert!(ContainerImage::parse(image).is_err());
96 assert!(serde_json::from_value::<ContainerImage>(serde_json::json!(image)).is_err());
97 }
98 let image = format!("sha256:{}", "a".repeat(64));
99 let parsed = ContainerImage::parse(&image).unwrap();
100 assert_eq!(
101 serde_json::from_value::<ContainerImage>(serde_json::json!(image)).unwrap(),
102 parsed
103 );
104 assert_eq!(
105 serde_json::to_value(parsed).unwrap(),
106 serde_json::json!(image)
107 );
108 }
109}