feat: Allow use of akmods module (#71)

The akmods module require having the /rpms directory put into /tmp/rpms.
By default we will mount the akmods image with the `main-{{ os_version
}}` tag.

If a user supplies `base` for the akmods module in their recipe, it will
pull that image tag instead and mount the resulting /rpms.

```yaml
modules:
- type: akmods
  base: surface
  install:
  - openrazer
```

This would pull the image `ghcr.io/ublue-os/akmods:surface-39`.

A user can also supply `nvidia-version` with the numerical version of
the driver you would like to use. Doing so will mount the appropriate
`akmods-nvidia` image with the version of the driver you want in the
tag.

```yaml
modules:
- type: akmods
  nvidia-version: 545
  install:
  - nvidia
```

This would pull the image `ghcr.io/ublue-os/akmods-nvidia:main-39-545`
and `ghcr.io/ublue-os/akmods:main-39`.

This uses bind mount like all the other modules so these files will not
persist into the final image.
This commit is contained in:
Gerald Pinder 2024-02-22 13:41:45 -05:00 committed by GitHub
parent ee2a834b28
commit 8931a22e29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 14 deletions

View file

@ -4,12 +4,21 @@ name: template
description: This is my personal OS image.
# the base image to build on top of (FROM) and the version tag to use
base-image: ghcr.io/ublue-os/silverblue-main
base-image: ghcr.io/ublue-os/silverblue-surface
image-version: 39 # latest is also supported if you want new updates ASAP
# module configuration, executed in order
# you can include multiple instances of the same module
modules:
- type: akmods
base: surface
nvidia-version: 545
install:
- nvidia
- openrazer
- type: akmods
install:
- openrgb
- type: files
files:
- usr: /usr # copies config/files/usr into your image's /usr.

View file

@ -1,4 +1,5 @@
use std::{
collections::HashSet,
env, fs,
path::{Path, PathBuf},
process,
@ -13,7 +14,7 @@ use uuid::Uuid;
use crate::{
constants::*,
module_recipe::{Module, Recipe},
module_recipe::{Module, ModuleExt, Recipe},
};
use super::BlueBuildCommand;
@ -166,7 +167,7 @@ fn get_containerfile_snippets(module: &Module) -> Option<Vec<String>> {
#[must_use]
fn print_containerfile(containerfile: &str) -> String {
debug!("print_containerfile({containerfile})");
trace!("print_containerfile({containerfile})");
debug!("Loading containerfile contents for {containerfile}");
let path = format!("config/containerfiles/{containerfile}/Containerfile");
@ -222,3 +223,56 @@ fn get_gitlab_registry_path() -> Option<String> {
.to_lowercase(),
)
}
#[derive(Debug, Clone, TypedBuilder, PartialEq, Eq, Hash)]
struct AkmodsInfo {
images: (String, Option<String>),
stage_name: String,
}
fn get_akmods_info_list(module_ext: &ModuleExt, os_version: &str) -> Vec<AkmodsInfo> {
trace!("get_akmods_image_list({module_ext:#?}, {os_version})");
let mut seen = HashSet::new();
module_ext
.modules
.iter()
.filter(|module| module.module_type.as_ref().is_some_and(|t| t == "akmods"))
.map(|module| generate_akmods_info(module, os_version))
.filter(|image| seen.insert(image.clone()))
.collect()
}
fn generate_akmods_info(module: &Module, os_version: &str) -> AkmodsInfo {
trace!("generate_akmods_base({module:#?}, {os_version})");
let base = module
.config
.get("base")
.map(|b| b.as_str().unwrap_or_default());
let nvidia_version = module
.config
.get("nvidia-version")
.map(|v| v.as_u64().unwrap_or_default());
AkmodsInfo::builder()
.images(match (base, nvidia_version) {
(Some(b), Some(nv)) if !b.is_empty() && nv > 0 => (
format!("akmods:{b}-{os_version}"),
Some(format!("akmods-nvidia:{b}-{os_version}-{nv}")),
),
(Some(b), _) if !b.is_empty() => (format!("akmods:{b}-{os_version}"), None),
(_, Some(nv)) if nv > 0 => (
format!("akmods:main-{os_version}"),
Some(format!("akmods-nvidia:main-{os_version}")),
),
_ => (format!("akmods:main-{os_version}"), None),
})
.stage_name(format!(
"{}{}",
base.unwrap_or("main"),
nvidia_version.map_or_else(String::default, |nv| format!("-{nv}"))
))
.build()
}

View file

@ -153,7 +153,7 @@ impl<'a> Recipe<'a> {
Ok(recipe)
}
fn get_os_version(&self) -> String {
pub fn get_os_version(&self) -> String {
trace!("Recipe::get_os_version()");
if check_command_exists("skopeo").is_err() {

View file

@ -1,3 +1,4 @@
{%- let os_version = recipe.get_os_version() %}
# This stage is responsible for holding onto
# your config without copying it directly into
# the final image
@ -11,6 +12,14 @@ FROM scratch as stage-modules
COPY --from=ghcr.io/ublue-os/bling:latest /modules /modules
COPY ./modules /modules
{%- for info in self::get_akmods_info_list(recipe.modules_ext, os_version) %}
FROM scratch as stage-akmods-{{ info.stage_name }}
COPY --from=ghcr.io/ublue-os/{{ info.images.0 }} /rpms /rpms
{%- if let Some(nv_image) = info.images.1 %}
COPY --from=ghcr.io/ublue-os/{{ nv_image }} /rpms /rpms
{%- endif %}
{%- endfor %}
# This stage is responsible for holding onto
# exports like the exports.sh
FROM docker.io/alpine as stage-exports
@ -37,16 +46,6 @@ ARG IMAGE_REGISTRY=localhost
COPY cosign.pub /usr/share/ublue-os/cosign.pub
{%- endif %}
COPY --from=docker.io/mikefarah/yq /usr/bin/yq /usr/bin/yq
COPY --from=gcr.io/projectsigstore/cosign /ko-app/cosign /usr/bin/cosign
COPY --from=ghcr.io/blue-build/cli:
{%- if let Some(tag) = recipe.blue_build_tag -%}
{{ tag }}
{%- else -%}
latest-installer
{%- endif %} /out/bluebuild /usr/bin/bluebuild
ARG CONFIG_DIRECTORY="/tmp/config"
ARG IMAGE_NAME="{{ recipe.name }}"
ARG BASE_IMAGE="{{ recipe.base_image }}"
@ -74,12 +73,16 @@ COPY {{ src }} {{ dest }}
RUN \
--mount=type=tmpfs,target=/tmp \
--mount=type=tmpfs,target=/var \
--mount=type=bind,from=docker.io/mikefarah/yq,src=/usr/bin/yq,dst=/usr/bin/yq \
--mount=type=bind,from=stage-config,src=/config,dst=/tmp/config,rw \
{%- if let Some(source) = module.source %}
--mount=type=bind,from={{ source }},src=/modules,dst=/tmp/modules,rw \
{%- else %}
--mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \
{%- endif %}
{%- if type == "akmods" %}
--mount=type=bind,from=stage-akmods-{{ self::generate_akmods_info(module, os_version).stage_name }},src=/rpms,dst=/tmp/rpms,rw \
{%- endif %}
--mount=type=bind,from=stage-exports,src=/exports.sh,dst=/tmp/exports.sh \
--mount=type=cache,dst=/var/cache/rpm-ostree,id=rpm-ostree-cache-{{ recipe.name }}-{{ recipe.image_version }},sharing=locked \
chmod +x /tmp/modules/{{ type }}/{{ type }}.sh \
@ -88,4 +91,12 @@ RUN \
{%- endif %}
{%- endfor %}
COPY --from=gcr.io/projectsigstore/cosign /ko-app/cosign /usr/bin/cosign
COPY --from=ghcr.io/blue-build/cli:
{%- if let Some(tag) = recipe.blue_build_tag -%}
{{ tag }}
{%- else -%}
latest-installer
{%- endif %} /out/bluebuild /usr/bin/bluebuild
RUN ostree container commit