diff --git a/recipe/src/recipe.rs b/recipe/src/recipe.rs index 85168973..8400133e 100644 --- a/recipe/src/recipe.rs +++ b/recipe/src/recipe.rs @@ -39,6 +39,11 @@ pub struct Recipe<'a> { #[builder(setter(into))] pub base_image: Cow<'a, str>, + /// The base image type for user's image. + #[serde(alias = "base-image-type", skip_serializing_if = "Option::is_none")] + #[builder(setter(into))] + pub base_image_type: Option>, + /// The version/tag of the base image. #[serde(alias = "image-version")] #[builder(setter(into))] diff --git a/src/commands/generate.rs b/src/commands/generate.rs index 1f3170cb..73af20e6 100644 --- a/src/commands/generate.rs +++ b/src/commands/generate.rs @@ -4,7 +4,9 @@ use std::{ }; use blue_build_recipe::Recipe; -use blue_build_template::{ContainerFileTemplate, Template}; +use blue_build_template::{ + ContainerFileTemplate, OstreeContainerFileTemplate, VanillaContainerFileTemplate, +}; use blue_build_utils::{ constants::{ CI_PROJECT_NAME, CI_PROJECT_NAMESPACE, CI_REGISTRY, CONFIG_PATH, GITHUB_REPOSITORY_OWNER, @@ -114,11 +116,40 @@ impl GenerateCommand { info!("Templating for recipe at {}", recipe_path.display()); - let template = ContainerFileTemplate::builder() - .os_version(Driver::get_os_version(&recipe_de)?) + let template: Box = match &recipe_de.base_image_type { + Some(cow) => match cow.as_ref() { + "vanilla" => Box::new(self.build_vanilla_template(&recipe_de, &recipe_path)?), + "ostree" => Box::new(self.build_ostree_template(&recipe_de, &recipe_path)?), + _ => Box::new(self.build_ostree_template(&recipe_de, &recipe_path)?), + }, + None => Box::new(self.build_ostree_template(&recipe_de, &recipe_path)?), + }; + + let output_str = template.render().into_diagnostic()?; + if let Some(output) = self.output.as_ref() { + debug!("Templating to file {}", output.display()); + trace!("Containerfile:\n{output_str}"); + + std::fs::write(output, output_str).into_diagnostic()?; + } else { + debug!("Templating to stdout"); + syntax_highlighting::print(&output_str, "Dockerfile", self.syntax_theme)?; + } + + Ok(()) + } + + fn build_ostree_template<'a>( + &self, + recipe_de: &'a Recipe<'a>, + recipe_path: &'a Path, + ) -> Result> { + info!("Using ostree template"); + Ok(OstreeContainerFileTemplate::builder() + .os_version(Driver::get_os_version(recipe_de)?) .build_id(Driver::get_build_id()) - .recipe(&recipe_de) - .recipe_path(recipe_path.as_path()) + .recipe(recipe_de) + .recipe_path(recipe_path) .registry(self.get_registry()) .exports_tag({ #[allow(clippy::const_is_empty)] @@ -131,20 +162,33 @@ impl GenerateCommand { shadow::COMMIT_HASH.to_string() } }) - .build(); - - let output_str = template.render().into_diagnostic()?; - if let Some(output) = self.output.as_ref() { - debug!("Templating to file {}", output.display()); - trace!("Containerfile:\n{output_str}"); - - std::fs::write(output, output_str).into_diagnostic()?; - } else { - debug!("Templating to stdout"); - syntax_highlighting::print(&output_str, "Dockerfile", self.syntax_theme)?; - } + .build()) + } - Ok(()) + fn build_vanilla_template<'a>( + &self, + recipe_de: &'a Recipe<'a>, + recipe_path: &'a Path, + ) -> Result> { + info!("Using vanilla template"); + Ok(VanillaContainerFileTemplate::builder() + .os_version(Driver::get_os_version(recipe_de)?) + .build_id(Driver::get_build_id()) + .recipe(recipe_de) + .recipe_path(recipe_path) + .registry(self.get_registry()) + .exports_tag({ + #[allow(clippy::const_is_empty)] + if shadow::COMMIT_HASH.is_empty() { + // This is done for users who install via + // cargo. Cargo installs do not carry git + // information via shadow + format!("v{}", crate_version!()) + } else { + shadow::COMMIT_HASH.to_string() + } + }) + .build()) } fn get_registry(&self) -> String { diff --git a/src/drivers.rs b/src/drivers.rs index cc0bfc24..f076367d 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -11,7 +11,7 @@ use std::{ }; use blue_build_recipe::Recipe; -use blue_build_utils::constants::IMAGE_VERSION_LABEL; +// use blue_build_utils::constants::IMAGE_VERSION_LABEL; use log::{debug, info, trace}; use miette::{bail, miette, Result}; use once_cell::sync::Lazy; @@ -389,12 +389,7 @@ impl Driver<'_> { .build(); let inspection = INSPECT_DRIVER.get_metadata(&inspect_opts)?; - let os_version = inspection.get_version().ok_or_else(|| { - miette!( - help = format!("Please check with the image author about using '{IMAGE_VERSION_LABEL}' to report the os version."), - "Unable to get the OS version from the labels" - ) - })?; + let os_version = inspection.get_version().unwrap_or(0); trace!("os_version: {os_version}"); os_version diff --git a/template/src/lib.rs b/template/src/lib.rs index f7bc0fee..0aeebff4 100644 --- a/template/src/lib.rs +++ b/template/src/lib.rs @@ -12,9 +12,20 @@ use uuid::Uuid; pub use rinja::Template; +pub trait ContainerFileTemplate { + /// # Errors + /// + /// Will return error from rinja if there is an issue rendering the template. + fn render(&self) -> Result; +} + #[derive(Debug, Clone, Template, TypedBuilder)] -#[template(path = "Containerfile.j2", escape = "none", whitespace = "minimize")] -pub struct ContainerFileTemplate<'a> { +#[template( + path = "Containerfile.ostree.j2", + escape = "none", + whitespace = "minimize" +)] +pub struct OstreeContainerFileTemplate<'a> { recipe: &'a Recipe<'a>, #[builder(setter(into))] @@ -31,7 +42,40 @@ pub struct ContainerFileTemplate<'a> { #[builder(setter(into))] exports_tag: Cow<'a, str>, } +impl ContainerFileTemplate for OstreeContainerFileTemplate<'_> { + fn render(&self) -> Result { + Template::render(&self) + } +} + +#[derive(Debug, Clone, Template, TypedBuilder)] +#[template( + path = "Containerfile.vanilla.j2", + escape = "none", + whitespace = "minimize" +)] +pub struct VanillaContainerFileTemplate<'a> { + recipe: &'a Recipe<'a>, + + #[builder(setter(into))] + recipe_path: &'a Path, + #[builder(setter(into))] + build_id: Uuid, + + os_version: u64, + + #[builder(setter(into))] + registry: Cow<'a, str>, + + #[builder(setter(into))] + exports_tag: Cow<'a, str>, +} +impl ContainerFileTemplate for VanillaContainerFileTemplate<'_> { + fn render(&self) -> Result { + Template::render(&self) + } +} #[derive(Debug, Clone, Template, TypedBuilder)] #[template(path = "github_issue.j2", escape = "md")] pub struct GithubIssueTemplate<'a> { diff --git a/template/templates/Containerfile.j2 b/template/templates/Containerfile.ostree.j2 similarity index 95% rename from template/templates/Containerfile.j2 rename to template/templates/Containerfile.ostree.j2 index e83e2087..78f98ca7 100644 --- a/template/templates/Containerfile.j2 +++ b/template/templates/Containerfile.ostree.j2 @@ -28,7 +28,7 @@ RUN --mount=type=bind,from=stage-bins,src=/bins,dst=/tmp/bins \ && cp /tmp/bins/* /usr/bin/ \ && ostree container commit -{% call modules::main_modules_run(recipe.modules_ext, os_version) %} +{% call modules::ostree_modules_run(recipe.modules_ext, os_version) %} RUN rm -fr /tmp/* /var/* && ostree container commit diff --git a/template/templates/Containerfile.vanilla.j2 b/template/templates/Containerfile.vanilla.j2 new file mode 100644 index 00000000..46ff8440 --- /dev/null +++ b/template/templates/Containerfile.vanilla.j2 @@ -0,0 +1,59 @@ +{%- import "modules/modules.j2" as modules -%} +{%- include "stages.j2" %} + +# Main image +FROM {{ recipe.base_image }}:{{ recipe.image_version }} AS {{ recipe.name|replace('/', "-") }} + +ARG RECIPE={{ recipe_path.display() }} +ARG IMAGE_REGISTRY={{ registry }} + +{%- if self::files_dir_exists() %} +ARG CONFIG_DIRECTORY="/tmp/files" +{%- else if self::config_dir_exists() %} +ARG CONFIG_DIRECTORY="/tmp/config" +{%- endif %} +ARG MODULE_DIRECTORY="/tmp/modules" +ARG IMAGE_NAME="{{ recipe.name }}" +ARG BASE_IMAGE="{{ recipe.base_image }}" + +# Key RUN +# RUN --mount=type=bind,from=stage-keys,src=/keys,dst=/tmp/keys \ +# mkdir -p /usr/etc/pki/containers/ \ +# && cp /tmp/keys/* /usr/etc/pki/containers/ + +# Bin RUN +RUN --mount=type=bind,from=stage-bins,src=/bins,dst=/tmp/bins \ + mkdir -p /usr/bin/ \ + && cp /tmp/bins/* /usr/bin/ + +# Init step copied from VanillaOS template +RUN lpkg --unlock && apt-get update + +{% call modules::generic_modules_run(recipe.modules_ext, os_version) %} + +# Cleanup step copied from VanillaOS template +RUN apt-get autoremove -y && apt-get clean && lpkg --lock + +# FsGuard step copied from VanillaOS template +# first download the required python script from the vib-fsguard module and the FsGuard binary +RUN mkdir -p /sources/fsguard/ && \ + wget https://github.com/linux-immutability-tools/FsGuard/releases/download/v0.1.2-2/FsGuard_0.1.2-2_linux_amd64.tar.gz -O /tmp/fsguard.tar.gz && tar -xf /tmp/fsguard.tar.gz -C /sources/fsguard/ && \ + curl https://raw.githubusercontent.com/Vanilla-OS/vib-fsguard/main/genfilelist.py -o /sources/fsguard/genfilelist.py && \ + rm -rf /FsGuard && rm -f ./minisign.pub ./minisign.key && chmod +x /usr/sbin/init && mkdir /FsGuard && \ + chmod +x /sources/fsguard/genfilelist.py && minisign -WG -s ./minisign.key && \ + python3 /sources/fsguard/genfilelist.py /usr/bin /FsGuard/filelist /usr/sbin/FsGuard && \ + minisign -Sm /FsGuard/filelist -p .//minisign.pub -s .//minisign.key && touch /FsGuard/signature && \ + echo -n "----begin attach----" >> /FsGuard/signature && cat /FsGuard/filelist.minisig >> /FsGuard/signature && \ + echo -n "----begin second attach----" >> /FsGuard/signature && tail -n1 .//minisign.pub >> /FsGuard/signature && \ + cat /FsGuard/signature >> /sources/fsguard/FsGuard && mv /sources/fsguard/FsGuard /usr/sbin/FsGuard && rm ./minisign.key ./minisign.pub + +RUN rm -fr /tmp/* /var/tmp/* /sources/* + +# Labels are added last since they cause cache misses with buildah +LABEL {{ blue_build_utils::constants::BUILD_ID_LABEL }}="{{ build_id }}" +LABEL org.opencontainers.image.title="{{ recipe.name }}" +LABEL org.opencontainers.image.description="{{ recipe.description }}" +{%- if let Some(repo) = self::get_repo_url() %} +LABEL org.opencontainers.image.source="{{ repo }}" +{%- endif %} +LABEL io.artifacthub.package.readme-url=https://raw.githubusercontent.com/blue-build/cli/main/README.md \ No newline at end of file diff --git a/template/templates/modules/modules.j2 b/template/templates/modules/modules.j2 index c2236652..fdcb7491 100644 --- a/template/templates/modules/modules.j2 +++ b/template/templates/modules/modules.j2 @@ -1,7 +1,8 @@ -{% macro main_modules_run(modules_ext, os_version) %} +{% macro generic_modules_run(modules_ext, os_version) %} # Module RUNs {%- for module in modules_ext.modules %} {%- if let Some(module) = module.required_fields %} + {%- if module.no_cache %} ARG CACHEBUST="{{ build_id }}" {%- endif %} @@ -22,22 +23,18 @@ RUN \ {%- else %} --mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \ {%- endif %} - {%- if module.module_type == "akmods" %} - --mount=type=bind,from=stage-akmods-{{ module.generate_akmods_info(os_version).stage_name }},src=/rpms,dst=/tmp/rpms,rw \ - {%- endif %} --mount=type=bind,from=ghcr.io/blue-build/cli:{{ exports_tag }}-build-scripts,src=/scripts/,dst=/tmp/scripts/ \ - --mount=type=cache,dst=/var/cache/rpm-ostree,id=rpm-ostree-cache-{{ recipe.name }}-{{ recipe.image_version }},sharing=locked \ - /tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module.print_module_context() }}' \ - && ostree container commit + /tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module.print_module_context() }}' {%- endif %} {%- endif %} {%- endfor %} {% endmacro %} -{% macro stage_modules_run(modules_ext, os_version) %} + + +{% macro ostree_modules_run(modules_ext, os_version) %} # Module RUNs {%- for module in modules_ext.modules %} {%- if let Some(module) = module.required_fields %} - {%- if module.no_cache %} ARG CACHEBUST="{{ build_id }}" {%- endif %} @@ -58,9 +55,14 @@ RUN \ {%- else %} --mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \ {%- endif %} + {%- if module.module_type == "akmods" %} + --mount=type=bind,from=stage-akmods-{{ module.generate_akmods_info(os_version).stage_name }},src=/rpms,dst=/tmp/rpms,rw \ + {%- endif %} --mount=type=bind,from=ghcr.io/blue-build/cli:{{ exports_tag }}-build-scripts,src=/scripts/,dst=/tmp/scripts/ \ - /tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module.print_module_context() }}' + --mount=type=cache,dst=/var/cache/rpm-ostree,id=rpm-ostree-cache-{{ recipe.name }}-{{ recipe.image_version }},sharing=locked \ + /tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module.print_module_context() }}' \ + && ostree container commit {%- endif %} {%- endif %} {%- endfor %} -{% endmacro %} +{% endmacro %} \ No newline at end of file diff --git a/template/templates/stages.j2 b/template/templates/stages.j2 index c1a689a1..9505e9fb 100644 --- a/template/templates/stages.j2 +++ b/template/templates/stages.j2 @@ -73,7 +73,7 @@ SHELL ["bash", "-c"] {%- endif %} {%- endif %} - {% call modules::stage_modules_run(stage.modules_ext, os_version) %} + {% call modules::generic_modules_run(stage.modules_ext, os_version) %} {%- endif %} {%- endfor %} {%- endif %}