Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions recipe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod akmods_info;
mod maybe_version;
mod module;
mod module_ext;
mod mount;
mod recipe;
mod stage;
mod stages_ext;
Expand Down
104 changes: 104 additions & 0 deletions recipe/src/mount.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum MountCacheSharing {
/// The cache is shared between all builds.
#[serde(rename = "shared")]
Shared,

/// The cache is private to the current build.
#[serde(rename = "private")]
Private,

/// The cache is shared between builds, but only one build can use it at a time.
#[serde(rename = "locked")]
Locked,
}
impl std::fmt::Display for MountCacheSharing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Shared => write!(f, "shared"),
Self::Private => write!(f, "private"),
Self::Locked => write!(f, "locked"),
}
}
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
pub enum Mount {
/// A bind mount, which mounts a file or directory from the host.
#[serde(rename = "bind")]
Bind {
/// The source path on the host.
source: String,
/// The destination path in the container.
destination: String,
/// Whether the mount is read-only.
#[serde(skip_serializing_if = "Option::is_none")]
read_only: Option<bool>,
Comment thread
Aex12 marked this conversation as resolved.
Outdated
},

/// A tmpfs mount, which mounts a temporary file system in memory.
#[serde(rename = "tmpfs")]
Tmpfs {
/// The destination path.
destination: String,
/// The size of the tmpfs. Can be specified in bytes or with a suffix (e.g. "100m" for 100 megabytes).
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<String>,
},

/// A cache mount, which mounts a cache directory that can be shared between builds.
#[serde(rename = "cache")]
Cache {
/// The destination path.
destination: String,
/// The cache ID, which is used to identify the cache.
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
/// The cache sharing mode.
#[serde(skip_serializing_if = "Option::is_none")]
sharing: Option<MountCacheSharing>,
},
}

impl std::fmt::Display for Mount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bind {
source,
destination,
read_only,
} => {
write!(f, "type=bind,source={source},dst={destination}")?;
if let Some(read_only) = read_only
&& *read_only
{
write!(f, ",readonly")?;
}
}
Self::Tmpfs { destination, size } => {
write!(f, "type=tmpfs,dst={destination}")?;
if let Some(size) = size {
write!(f, ",size={size}")?;
}
}
Self::Cache {
destination,
id,
sharing,
} => {
write!(f, "type=cache")?;
if let Some(sharing) = sharing {
write!(f, ",sharing={sharing}")?;
}
write!(f, ",dst={destination}")?;
if let Some(id) = id {
write!(f, ",id={id}")?;
}
}
}
Ok(())
}
}
6 changes: 5 additions & 1 deletion recipe/src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use miette::{Context, IntoDiagnostic, Result};
use oci_client::Reference;
use serde::{Deserialize, Serialize};

use crate::{Module, ModuleExt, StagesExt, maybe_version::MaybeVersion};
use crate::{Module, ModuleExt, StagesExt, maybe_version::MaybeVersion, mount::Mount};

/// The build recipe.
///
Expand Down Expand Up @@ -90,6 +90,10 @@ pub struct Recipe {
/// This hashmap provides custom labels from ther use to the image
#[serde(skip_serializing_if = "Option::is_none")]
pub labels: Option<HashMap<String, String>>,

/// The mounts to add to the image.
#[serde(skip_serializing_if = "Option::is_none")]
pub mounts: Option<Vec<Mount>>,
Comment thread
Aex12 marked this conversation as resolved.
Outdated
Comment thread
Aex12 marked this conversation as resolved.
Outdated
}

impl Recipe {
Expand Down
5 changes: 5 additions & 0 deletions template/templates/modules/modules.j2
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ RUN \
{{ cache_mount }},dst=/var/cache/apt,id=apt-{{ cache_name }} \
{{ cache_mount }},dst=/var/cache/pacman,id=pacman-{{ cache_name }} \

{#- Recipe defined mounts #}
{%- for mount in recipe.mounts.iter().flatten() %}
--mount={{ mount.to_string() }} \
{%- endfor %}

{#- Secret environment variables #}
{%- for secret_var in module.secrets.envs() %}
{{ secret_var }} \
Expand Down
7 changes: 7 additions & 0 deletions test-files/recipes/recipe-pass.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ modules:
from: fedora-test
src: /test.txt
dest: /
mounts:
- type: cache
id: downloads-cli-test-43
destination: /var/cache/downloads
sharing: locked
- type: tmpfs
destination: /tmp
103 changes: 103 additions & 0 deletions test-files/schema/recipe-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
"$ref": "#/$defs/ModuleEntry"
},
"description": "A list of [modules](https://blue-build.org/reference/module/) that is executed in order. Multiple of the same module can be included.\n\nEach item in this list should have at least a `type:` or be specified to be included from an external file in the `recipes/` directory with `from-file:`."
},
"mounts": {
"type": "array",
"items": {
"$ref": "#/$defs/Mount"
},
"description": "A list of mounts that will be mounted in each RUN stage."
}
},
"required": [
Expand Down Expand Up @@ -81,6 +88,102 @@
}
]
},
"Mount": {
"oneOf": [
{
"$ref": "#/$defs/MountBind"
},
{
"$ref": "#/$defs/MountTmpFs"
},
{
"$ref": "#/$defs/MountCache"
}
]
},
"MountBind": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "bind",
"description": "A bind mount from the host system."
},
"source": {
"type": "string",
"description": "The source path on the host system to mount."
},
"destination": {
"type": "string",
"description": "The destination path to mount to."
},
"readOnly": {
"type": "boolean",
"description": "Whether the mount should be read-only."
}
},
"required": [
"type",
"source",
"destination"
]
},
"MountTmpFs": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "tmpfs",
"description": "A temporary file system mount."
},
"destination": {
"type": "string",
"description": "The destination path to mount to."
},
"size": {
"type": "string",
"description": "The size of the tmpfs mount."
}
},
"required": [
"type",
"destination"
]
},
"MountCache": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "cache",
"description": "A cache mount."
},
"destination": {
"type": "string",
"description": "The destination path to mount to."
},
"id": {
"type": "string",
"description": "An identifier for the cache."
},
"sharing": {
"$ref": "#/$defs/MountCacheSharing",
"description": "The sharing mode of the cache."
}
},
"required": [
"type",
"destination"
]
},
"MountCacheSharing": {
"type": "string",
"enum": [
"shared",
"private",
"locked"
]
},
"ImportedModule": {
"type": "object",
"properties": {
Expand Down
Loading