diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..53649aa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+.idea
+*.tar.gz
+.vscode
+ctr-bundle
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..798176b
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,358 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d76c22c9b9b215eeb8d016ad3a90417bd13cb24cf8142756e6472445876cab7"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "indexmap",
+ "lazy_static",
+ "os_str_bytes",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+dependencies = [
+ "cfg-if",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
+
+[[package]]
+name = "log"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quark"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "flate2",
+ "log",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+
+[[package]]
+name = "serde"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+
+[[package]]
+name = "thiserror"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..036614f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "quark"
+version = "0.1.0"
+edition = "2021"
+authors = ["Polytech Montpellier - DevOps"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "3.0.5", features = ["derive"] }
+flate2 = "1.0"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+thiserror = "1.0.30"
+log = "0.4.16"
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3967287
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+.PHONY: build
+
+build:
+ cargo build
+
+pre-test:
+ @echo "Seting tests environment..."
+ @mkdir -p /tmp/quark/builds/testquardle/dossier1/
+ @touch /tmp/quark/builds/testquardle/fichier1.txt
+ @touch /tmp/quark/builds/testquardle/fichier2.txt
+ @touch /tmp/quark/builds/testquardle/dossier1/fichier3.txt
+ @echo "Done. Your quadle env is named: testquardle"
+
+test: pre-test
+ @echo "Running tests..."
+ cargo run -- build --image sometest --quardle testquardle
+ mkdir out/testquardle && tar -C out/testquardle -xvf out/testquardle.qrk
diff --git a/README.md b/README.md
index 8f9a918..dba7367 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,15 @@
-# Lumper
+# Quark
+
+**quark** is a CLI tool for building and running images for lumper to boot.
+
+## How to use
+
+Building a quardle with the container image bundled into the initramfs image:
+`$ quark build --image --offline --quardle `
+
+Building a quardle with the container image to be pulled from within the guest:
+`$ quark build --image --quardle `
+
+This commands will create a quardle with the name `.qrk`
diff --git a/out/.gitkeep b/out/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/cli/build.rs b/src/cli/build.rs
new file mode 100644
index 0000000..c6a583c
--- /dev/null
+++ b/src/cli/build.rs
@@ -0,0 +1,41 @@
+use crate::quardle::Quardle;
+use crate::{Handler, Result};
+use clap::Args;
+
+/// CLI related errors
+#[derive(Debug)]
+pub enum Error {
+}
+
+/// Arguments for our `BuildCommand`.
+///
+/// These arguments are parsed by `clap` and an instance of `BuildCommand` containing
+/// arguments is provided.
+///
+/// Example :
+///
+/// `quark build --image --quardle `
+///
+/// The `handler` method provided below will be executed.
+#[derive(Debug, Args)]
+pub struct BuildCommand {
+ /// The url of the container image
+ #[clap(short, long)]
+ image: String,
+ /// The name of the quardle to create
+ #[clap(short, long)]
+ quardle: String,
+
+ /// The name of the quardle to create
+ #[clap(short, long)]
+ offline: bool
+}
+
+impl Handler for BuildCommand {
+ fn handler(&self) -> Result<()> {
+ Quardle::new(self.quardle.clone(), self.image.clone(), self.offline)
+ .unwrap()
+ .build(Some(false)).unwrap();
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
new file mode 100644
index 0000000..d83a4df
--- /dev/null
+++ b/src/cli/mod.rs
@@ -0,0 +1,60 @@
+mod build;
+
+use crate::cli::build::BuildCommand;
+use clap::{Parser, Subcommand};
+
+/// CLI related errors
+#[derive(Debug)]
+pub enum Error {
+}
+
+/// A common result type for our CLI.
+pub type Result = std::result::Result;
+
+/// `Handler` is a trait that should be implemented for each of our commands.
+///
+/// It defines the contract & the input / output of a command execution.
+pub trait Handler {
+ /// Executes the command handler.
+ ///
+ /// Every command should take no argument, has it is built at runtime with these arguments.
+ /// Also, a command must always return a `Result<()>`.
+ fn handler(&self) -> crate::Result<()>;
+}
+
+#[derive(Parser, Debug)]
+#[clap(version, author)]
+pub struct Cli {
+ /// Container bundle
+ #[clap(subcommand)]
+ pub(crate) command: Command,
+}
+
+impl Cli {
+ /// Get the command used by the user.
+ ///
+ /// For example, if the user executes the command `build`,
+ /// we dynamically return the command so the `main` can
+ /// execute it.
+ pub fn command(self) -> Box {
+ match self.command {
+ Command::Build(cmd) => Box::new(cmd),
+ }
+ }
+}
+
+/// The enumeration of our commands.
+///
+/// Each of our commands should be listed in this enumeration with the following format :
+/// CommandName(CommandHandler)
+///
+/// Example:
+///
+/// You want to add the `list` command:
+///
+/// List(ListCommand)
+#[derive(Subcommand, Debug)]
+pub enum Command {
+ /// Build a quardle
+ Build(BuildCommand),
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..7f6572e
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,14 @@
+use clap::Parser;
+
+use crate::cli::{Cli, Handler, Result};
+
+mod cli;
+mod quardle;
+
+fn main() -> Result<()> {
+ let cli: Cli = Cli::parse();
+
+ cli.command().handler()?;
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/src/quardle/mod.rs b/src/quardle/mod.rs
new file mode 100644
index 0000000..d630522
--- /dev/null
+++ b/src/quardle/mod.rs
@@ -0,0 +1,376 @@
+use std::fs::{File, create_dir_all, remove_dir_all};
+use serde::{Deserialize, Serialize};
+use serde_json;
+use std::process::Command;
+use log::{info, warn};
+
+/// Constances declarations
+const QUARK_BUILD_DIR: &str = "/tmp/quark/builds/";
+const QUARK_CONFIG_DIR: &str = "/opt/quark/";
+
+const QUARDLE_KERNEL: &str = "vmlinux.bin";
+const QUARDLE_KERNEL_CMDLINE: &str = "/proc/cmdline";
+const QUARDLE_INITRD: &str = "initramfs.img";
+
+/// Containers related errors
+#[derive(Debug, thiserror::Error)]
+pub enum Error {}
+
+/// A common result type for our module.
+pub type Result = std::result::Result;
+
+#[derive(Serialize, Deserialize)]
+struct QuardleConfig {
+ name: String,
+ kernel: String,
+ initrd: String,
+ cmdline: String,
+ container_image_url: String,
+}
+
+/// The `Container` struct provides a simple way to
+/// create and run a container on the host.
+#[derive(Default, Debug, Clone)]
+pub struct Quardle {
+ name: String,
+ container_image_url: String,
+ offline: bool
+}
+
+impl Quardle {
+
+ /// Build a quardle from instance variables
+ /// If delete is true, delete the quardle temporary files after building
+ pub fn build(&self, delete_after: Option) -> Result<()> {
+ // creating working directory
+ create_dir_all(self.clone().get_work_dir()).unwrap();
+
+ self
+ .setup()
+ .add_kernel()
+ .add_initramfs()
+ .add_config_file()
+ .clean_quardle_build_dir()
+ .make_archive()?;
+
+ // Deleting temporary files used to build the quardle
+ if delete_after.unwrap_or(false) {
+ self.delete().unwrap();
+ }
+
+ Ok(())
+ }
+
+ /// Instanciate a new quardle
+ pub fn new(name: String, container_image_url: String, offline: bool) -> Option {
+ Some(Quardle {name, container_image_url, offline})
+ }
+
+ /// Delete all temporary files used to create quardle are created at /tmp/quark/builds//
+ pub fn delete(&self) -> Result<()> {
+ if !std::path::Path::new(format!("{}", self.clone().get_work_dir()).as_str()).exists() {
+ remove_dir_all(self.clone().get_work_dir()).unwrap();
+ }
+ Ok(())
+ }
+
+ /// Return the path to the quardle working directory
+ /// /tmp/quark/builds//
+ /// Used to create temporary files used to build quardle
+ fn get_work_dir(self) -> String {
+ format!("{}{}/", QUARK_BUILD_DIR, self.name)
+ }
+
+ /// Setup default quark configuration
+ /// Create a `quark` directory in /opt
+ fn setup(&self) -> &Quardle {
+ // Create the quark configuration directory
+ if !std::path::Path::new(&format!("{}", QUARK_CONFIG_DIR)).exists() {
+ warn!("Quark configuration directory does not exist, creating it !");
+ Command::new("mkdir")
+ .arg("/opt/quark")
+ .output()
+ .expect("failed to setup quark");
+ }
+
+ // Install kaps sources
+ if !std::path::Path::new(&format!("{}kaps", QUARK_CONFIG_DIR)).exists() {
+ info!("Kaps not found, installing it !");
+ Command::new("git")
+ .args(["clone", "https://github.com/virt-do/kaps.git"]) // Using https protocol because it seems not supporting ssh
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to fetch kaps");
+ }
+
+ // Install a default kernel configuration file
+ if !std::path::Path::new(&format!("{}linux-config-x86_64", QUARK_CONFIG_DIR)).exists() {
+ warn!("Kernel config file not found, installing it !");
+ Command::new("curl")
+ .arg("https://raw.githubusercontent.com/virt-do/lab/main/do-vmm/kernel/linux-config-x86_64")
+ .arg("-O")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to fetch kernel config file");
+ }
+
+ // Install a script to build kernel
+ if !std::path::Path::new(&format!("{}mkkernel.sh", QUARK_CONFIG_DIR)).exists() {
+ warn!("Kernel build script not found, installing it !");
+ Command::new("curl")
+ .arg("https://raw.githubusercontent.com/virt-do/lab/main/do-vmm/kernel/mkkernel.sh")
+ .arg("-O")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to fetch kernel build script");
+
+ Command::new("chmod")
+ .arg("+x")
+ .arg(format!("{}mkkernel.sh", QUARK_CONFIG_DIR))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to make kernel build script executable");
+ }
+
+ // Building kernel binary
+ if !std::path::Path::new(&format!("{}linux-cloud-hypervisor", QUARK_CONFIG_DIR)).exists() {
+ warn!("Kernel not builded, building it !");
+ Command::new(format!("{}mkkernel.sh", QUARK_CONFIG_DIR))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to build kernel");
+ }
+
+ // Install a script to build initramfs
+ if !std::path::Path::new(&format!("{}alpine-minirootfs", QUARK_CONFIG_DIR)).exists() {
+ warn!("Rootfs not builded, building it !");
+ Command::new("curl")
+ .arg("https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-minirootfs-3.14.2-x86_64.tar.gz")
+ .arg("-O")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to download rootfs archive");
+ Command::new("mkdir")
+ .arg("alpine-minirootfs")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to download initramfs build script");
+ Command::new("tar")
+ .arg("-xzf")
+ .arg("alpine-minirootfs-3.14.2-x86_64.tar.gz")
+ .arg("-C")
+ .arg("alpine-minirootfs")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to extract rootfs archive");
+
+ // Adding kaps binary to the rootfs
+ self.add_kaps_to_rootfs();
+ }
+
+ if !std::path::Path::new(&format!("{}mkinitramfs.sh", QUARK_CONFIG_DIR)).exists() {
+ warn!("InitramFS build script not found, installing it !");
+ Command::new("curl")
+ .arg("https://raw.githubusercontent.com/virt-do/quark/main/tools/mkinitramfs.sh")
+ .arg("-O")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to download initramfs build script");
+ Command::new("chmod")
+ .arg("+x")
+ .arg(format!("{}mkinitramfs.sh", QUARK_CONFIG_DIR))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to make initramfs build script executable");
+ }
+
+ // Install a script to build kaps bundle
+ if !std::path::Path::new(&format!("{}mkbundle.sh", QUARK_CONFIG_DIR)).exists() {
+ warn!("Kaps bundle build script not found, installing it !");
+ Command::new("curl")
+ .arg("https://raw.githubusercontent.com/virt-do/lab/main/do-vmm/rootfs/mkbundle.sh")
+ .arg("-O")
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to fetch kaps bundle build script");
+ Command::new("chmod")
+ .arg("+x")
+ .arg(format!("{}mkbundle.sh", QUARK_CONFIG_DIR))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to make kernel build script executable");
+ }
+ self
+ }
+
+ /// Append kernel configuration
+ /// Fetch automated script if isn't already installed, and use some bash script to build it
+ fn add_kernel(&self) -> &Quardle {
+ info!("Installing kernel binary !");
+ Command::new("cp")
+ .arg(format!("{}linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin", QUARK_CONFIG_DIR))
+ .arg(format!("{}", self.clone().get_work_dir()))
+ .spawn()
+ .expect("failed to copy kernel");
+ self
+ }
+
+ /// Append basic rootfs
+ /// Fetch automated script if isn't already installed, and use some bash script to build it
+ fn add_initramfs(&self) -> &Quardle {
+ info!("Installing initRamFS image to quardle");
+ Command::new("cp")
+ .arg("-r")
+ .arg(format!("{}alpine-minirootfs", QUARK_CONFIG_DIR))
+ .arg(format!("{}", self.clone().get_work_dir()))
+ .spawn()
+ .expect("failed to copy rootfs");
+
+ // If offline mode is active, we need to build kaps bundle image directly in the quardle.
+ if self.offline {
+ info!("Offline mode, adding kaps bundle to the quardle.");
+ Command::new(format!("{}mkbundle.sh", QUARK_CONFIG_DIR))
+ .arg(format!("{}/alpine-minirootfs/ctr-bundle", self.clone().get_work_dir()))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to build initramfs");
+ }
+
+ // add init file to it
+ info!("InitramFS not builded, building it !");
+ Command::new(format!("{}mkinitramfs.sh", QUARK_CONFIG_DIR))
+ .arg(format!("{}/alpine-minirootfs", self.clone().get_work_dir()))
+ .current_dir(format!("{}", QUARK_CONFIG_DIR))
+ .output()
+ .expect("failed to build initramfs");
+
+ self
+ }
+
+ fn clean_quardle_build_dir(&self) -> &Quardle {
+ info!("Cleaning quardle build directory !");
+ Command::new("rm")
+ .arg("-rdf")
+ .arg("alpine-minirootfs")
+ .arg("mkbundle.sh")
+ .arg("mkinitramfs.sh")
+ .current_dir(format!("{}", self.clone().get_work_dir()))
+ .output()
+ .expect("failed to clean quardle build directory");
+ self
+ }
+
+ /// Append kaps binary to rootfs image
+ /// Fetch kaps source code if isn't already installed, build it from source and copy it to the working directory
+ fn add_kaps_to_rootfs(&self) -> &Quardle {
+ info!("Installing kaps to quardle");
+ Command::new("cargo")
+ .current_dir(format!("{}kaps", QUARK_CONFIG_DIR))
+ .arg("build")
+ .arg("--release")
+ // .arg("--out-dir") //TODO: outdir is only available on nightly for now, should be used later
+ // .arg(format!("{}/rootfs/usr/bin/kaps",self.clone().get_work_dir()))
+ .output()
+ .expect("failed to build kaps");
+
+ Command::new("cp")
+ .arg(format!("{}kaps/target/release/kaps", QUARK_CONFIG_DIR))
+ .arg(format!("{}/alpine-minirootfs/usr/bin/kaps",self.clone().get_work_dir()))
+ .output()
+ .expect("failed to copy kaps");
+
+ self
+ }
+
+ /// Generate the config file of the quardle
+ /// The config file is a JSON file containing a QuardleConfig struct
+ fn add_config_file(&self) -> &Quardle {
+
+ let config = QuardleConfig {
+ name: self.name.clone(),
+ kernel: QUARDLE_KERNEL.to_string(),
+ initrd: QUARDLE_INITRD.to_string(),
+ cmdline: QUARDLE_KERNEL_CMDLINE.to_string(),
+ container_image_url: self.container_image_url.clone()
+ };
+
+ let config_json = serde_json::to_string(&config).unwrap();
+ let mut file = File::create(format!("{}quark.json",self.clone().get_work_dir())).unwrap();
+
+ use std::io::Write;
+ file.write_all(config_json.as_bytes()).unwrap();
+
+ self
+ }
+
+ /// Create compressed archive from quardle files and append it to /out/.qrk
+ fn make_archive(&self) -> Result<()> {
+ info!("Packaging quardle.");
+ Command::new("tar")
+ .arg("-zcvf")
+ .arg(format!("{}",format!("out/{}.qrk",self.name)))
+ .arg(format!("{}",self.clone().get_work_dir()))
+ .output()
+ .expect("failed to create archive");
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn quardle_new() {
+ let quardle = Quardle::new("test1".to_string(), "container1".to_string(), false);
+ assert_eq!(quardle.as_ref().unwrap().name, "test1");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container1");
+ }
+
+ #[test]
+ fn quardle_get_work_dir() {
+ let quardle = Quardle::new("this-should-be-a-directory".to_string(), "my-container".to_string(), false);
+ assert_eq!(quardle.unwrap().get_work_dir(), "/tmp/quark/builds/this-should-be-a-directory/");
+ }
+
+ #[test]
+ fn quardle_setup() {
+ let quardle = Quardle::new("test2".to_string(), "container2".to_string(), false);
+ quardle.as_ref().unwrap().setup();
+ assert_eq!(quardle.as_ref().unwrap().name, "test2");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container2");
+ }
+
+ #[test]
+ fn quardle_add_kernel() {
+ let quardle = Quardle::new("test3".to_string(), "container3".to_string(), false);
+ quardle.as_ref().unwrap().add_kernel();
+ assert_eq!(quardle.as_ref().unwrap().name, "test3");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container3");
+ }
+
+ #[test]
+ fn quardle_add_initramfs() {
+ let quardle = Quardle::new("test4".to_string(), "container4".to_string(), false);
+ quardle.as_ref().unwrap().add_initramfs();
+ assert_eq!(quardle.as_ref().unwrap().name, "test4");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container4");
+ }
+
+ #[test]
+ fn quardle_add_kaps_to_rootfs() {
+ let quardle = Quardle::new("test6".to_string(), "container6".to_string(), false);
+ quardle.as_ref().unwrap().add_initramfs().add_kaps_to_rootfs();
+ assert_eq!(quardle.as_ref().unwrap().name, "test6");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container6");
+ }
+
+ #[test]
+ #[should_panic]
+ fn quardle_clean_quardle_build_dir() {
+ let quardle = Quardle::new("test5".to_string(), "container5".to_string(), false);
+ quardle.as_ref().unwrap().clean_quardle_build_dir();
+ assert_eq!(quardle.as_ref().unwrap().name, "test5");
+ assert_eq!(quardle.as_ref().unwrap().container_image_url, "container5");
+ }
+}
\ No newline at end of file
diff --git a/tools/mkinitramfs.sh b/tools/mkinitramfs.sh
new file mode 100644
index 0000000..df975ba
--- /dev/null
+++ b/tools/mkinitramfs.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/bash
+DEST=${1:-"alpine-minirootfs"}
+
+pushd "$DEST"
+cat > init < ../initramfs.img
+
+popd
\ No newline at end of file