1- use std:: fs:: { File , create_dir_all, remove_dir_all, metadata } ;
1+ use std:: fs:: { File , create_dir_all, remove_dir_all} ;
22use flate2:: Compression ;
33use flate2:: write:: GzEncoder ;
44use serde:: { Deserialize , Serialize } ;
55use serde_json;
6+ use std:: process:: Command ;
67
78/// Constances declarations
8- const QUARDLE_BUILD_DIR : & str = "/tmp/quark/builds/" ;
9- const QUARDLE_KERNEL : & str = "linux-config-x86_64" ;
10- const QUARDLE_INITRD : & str = "quarkfs/" ;
9+ const QUARK_BUILD_DIR : & str = "/tmp/quark/builds/" ;
10+ const QUARK_CONFIG_DIR : & str = "/opt/quark/" ;
11+
12+ const QUARDLE_KERNEL : & str = "bzImage" ;
13+ const QUARDLE_INITRD : & str = "rootfs/" ;
1114const QUARDLE_KERNEL_CMDLINE : & str = "/proc/cmdline" ;
1215
1316/// Containers related errors
1417#[ derive( Debug , thiserror:: Error ) ]
1518pub enum Error {
16- #[ error( "Error when building quardle" ) ]
17- Build ( String ) ,
18- #[ error( "Unable to instanciate quardle" ) ]
19- New ( String ) ,
20- #[ error( "Error when deleting quardle" ) ]
21- Delete ( String ) ,
2219}
2320
2421/// A common result type for our module.
@@ -47,11 +44,20 @@ impl Quardle {
4744 /// Build a quardle from instance variables
4845 /// If delete is true, delete the quardle after building
4946 pub fn build ( & self , delete_after : Option < bool > ) -> Result < ( ) > {
50-
47+ // creating working directory
48+ create_dir_all ( self . clone ( ) . get_work_dir ( ) ) . unwrap ( ) ;
49+
50+ self
51+ . setup ( )
52+ . add_kernel ( )
53+ . add_init_rd ( )
54+ . add_kaps ( ) ;
55+ self . generate_config_file ( ) ?;
56+
5157 self . generate_archive ( ) ?;
5258
5359 if delete_after. unwrap_or ( false ) {
54- self . delete ( ) ? ;
60+ self . delete ( ) ;
5561 }
5662
5763 Ok ( ( ) )
@@ -61,28 +67,155 @@ impl Quardle {
6167 /// Temp files used to create quardle are created at /tmp/quark/builds/<quardle_name>/
6268 pub fn new ( name : String , container_image_url : String , offline : bool ) -> Option < Self > {
6369 let quardle = Quardle { name, container_image_url, offline} ;
64- create_dir_all ( quardle. clone ( ) . get_work_dir ( ) ) . unwrap ( ) ;
6570 Some ( quardle)
6671 }
6772
6873 /// Delete all temporary files used to create quardle are created at /tmp/quark/builds/<quardle_name>/
69- pub fn delete ( & self ) -> Result < ( ) > {
74+ pub fn delete ( & self ) -> & Quardle {
7075 remove_dir_all ( self . clone ( ) . get_work_dir ( ) ) . unwrap ( ) ;
71- Ok ( ( ) )
76+ self
7277 }
7378
7479 /// Return the path to the quardle working directory
7580 /// /tmp/quark/builds/<quardle_name>/
7681 /// Used to create temporary files
7782 fn get_work_dir ( self ) -> String {
78- format ! ( "{}{}/" , QUARDLE_BUILD_DIR , self . name)
83+ format ! ( "{}{}/" , QUARK_BUILD_DIR , self . name)
84+ }
85+
86+ /// Setup default quark configuration
87+ /// Create a `quark` directory in /opt
88+ fn setup ( & self ) -> & Quardle {
89+ // Create the quark configuration directory
90+ if !std:: path:: Path :: new ( & format ! ( "{}" , QUARK_CONFIG_DIR ) ) . exists ( ) {
91+ println ! ( "Quark configuration directory does not exist, creating it !" ) ;
92+ Command :: new ( "mkdir" )
93+ . arg ( "/opt/quark" )
94+ . output ( )
95+ . expect ( "failed to setup quark" ) ;
96+ }
97+
98+ // Install kaps sources
99+ if !std:: path:: Path :: new ( & format ! ( "{}kaps" , QUARK_CONFIG_DIR ) ) . exists ( ) {
100+ println ! ( "Kaps not found, installing it !" ) ;
101+ Command :: new ( "git" )
102+ . args ( [ "clone" , "https://github.com/virt-do/kaps.git" ] ) // Using https protocol because it seems not supporting ssh
103+ . current_dir ( format ! ( "{}" , QUARK_CONFIG_DIR ) )
104+ . output ( )
105+ . expect ( "failed to fetch kaps" ) ;
106+ }
107+
108+ // Install a default kernel configuration file
109+ if !std:: path:: Path :: new ( & format ! ( "{}linux-config-x86_64" , QUARK_CONFIG_DIR ) ) . exists ( ) {
110+ println ! ( "Kernel config file not found, installing it !" ) ;
111+ Command :: new ( "curl" )
112+ . arg ( "https://raw.githubusercontent.com/virt-do/lab/main/do-vmm/kernel/linux-config-x86_64" )
113+ . arg ( "-O" )
114+ . current_dir ( format ! ( "{}" , QUARK_CONFIG_DIR ) )
115+ . output ( )
116+ . expect ( "failed to fetch kernel config file" ) ;
117+ }
118+
119+ // Install a script to build kernel
120+ if !std:: path:: Path :: new ( & format ! ( "{}kernel" , QUARK_CONFIG_DIR ) ) . exists ( ) {
121+ println ! ( "Kernel not found, installing and building it !" ) ;
122+ Command :: new ( "git" )
123+ . arg ( "clone" )
124+ . args ( [ "--depth" , "1" ] )
125+ . arg ( "https://github.com/cloud-hypervisor/linux.git" ) // Using https protocol because it seems not supporting ssh
126+ . args ( [ "-b" , "ch-5.14" ] )
127+ . arg ( "kernel" )
128+ . current_dir ( format ! ( "{}" , QUARK_CONFIG_DIR ) )
129+ . output ( )
130+ . expect ( "failed to download kernel builder" ) ;
131+ // Use our config file
132+ Command :: new ( "cp" )
133+ . arg ( format ! ( "{}linux-config-x86_64" , QUARK_CONFIG_DIR ) )
134+ . arg ( format ! ( "{}kernel/.config" , QUARK_CONFIG_DIR ) )
135+ . output ( )
136+ . expect ( "failed to copy kernel config" ) ;
137+
138+ // Building kernel binary
139+ // FIXME: It seems that the build process is not working properly, command is stopped during build process
140+ Command :: new ( "make" )
141+ . arg ( "bzImage" )
142+ . args ( [ "-j" , "nproc" ] )
143+ . current_dir ( format ! ( "{}kernel" , QUARK_CONFIG_DIR ) )
144+ . status ( ) // Here we are using status because we have to wait the command to finish
145+ . expect ( "failed to build kernel binary" ) ;
146+ }
147+
148+ self
149+ }
150+
151+ /// Append kernel configuration
152+ /// Fetch automated script if isn't already installed, and use some bash script to build it
153+ fn add_kernel ( & self ) -> & Quardle {
154+ println ! ( "Installing kernel binary !" ) ;
155+ Command :: new ( "cp" )
156+ . arg ( format ! ( "{}arch/x86/boot/bzImage" , QUARK_CONFIG_DIR ) )
157+ . arg ( format ! ( "{}/" , self . clone( ) . get_work_dir( ) ) )
158+ . spawn ( )
159+ . expect ( "failed to copy kernel" ) ;
160+ self
161+ }
162+
163+ /// Append basic rootfs
164+ /// Fetch automated script if isn't already installed, and use some bash script to build it
165+ fn add_init_rd ( & self ) -> & Quardle {
166+ println ! ( "Installing initRamFS" ) ;
167+ Command :: new ( "curl" )
168+ . arg ( "https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-minirootfs-3.14.2-x86_64.tar.gz" )
169+ . arg ( "-O" )
170+ . current_dir ( self . clone ( ) . get_work_dir ( ) )
171+ . output ( )
172+ . expect ( "failed to download alpine image" ) ;
173+ Command :: new ( "mkdir" )
174+ . arg ( QUARDLE_INITRD )
175+ . current_dir ( self . clone ( ) . get_work_dir ( ) )
176+ . output ( )
177+ . expect ( "failed to create rootfs folder" ) ;
178+ Command :: new ( "tar" )
179+ . arg ( "-xf" )
180+ . arg ( "alpine-minirootfs-3.14.2-x86_64.tar.gz" )
181+ . args ( [ "-C" , QUARDLE_INITRD ] )
182+ . current_dir ( self . clone ( ) . get_work_dir ( ) )
183+ . output ( )
184+ . expect ( "failed to untar alpine image" ) ;
185+ Command :: new ( "rm" )
186+ . arg ( "-f" )
187+ . arg ( "alpine-minirootfs-3.14.2-x86_64.tar.gz" )
188+ . current_dir ( self . clone ( ) . get_work_dir ( ) )
189+ . output ( )
190+ . expect ( "failed to delete downloaded archive" ) ;
191+ Command :: new ( "runc" )
192+ . arg ( "spec" )
193+ . arg ( "--rootless" )
194+ . current_dir ( self . clone ( ) . get_work_dir ( ) )
195+ . output ( )
196+ . expect ( "failed to generate runtime spec" ) ;
197+ self
79198 }
80199
81200 /// Append kaps binary to builded image
82201 /// Fetch kaps source code if isn't already installed, build it from source and copy it to the working directory
83- // TODO: appends kaps binary at /opt/kaps/bin/kaps and set a ln -s /opt/kaps/bin/kaps /usr/bin/kaps
84- fn append_kaps ( & self ) -> Result < ( ) > {
85- Ok ( ( ) )
202+ fn add_kaps ( & self ) -> & Quardle {
203+ Command :: new ( "cargo" )
204+ . current_dir ( format ! ( "{}kaps" , QUARK_CONFIG_DIR ) )
205+ . arg ( "build" )
206+ . arg ( "--release" )
207+ // .arg("--out-dir") //TODO: outdir is only available on nightly for now, should be used later
208+ // .arg(format!("{}/rootfs/usr/bin/kaps",self.clone().get_work_dir()))
209+ . output ( )
210+ . expect ( "failed to build kaps" ) ;
211+
212+ Command :: new ( "cp" )
213+ . arg ( format ! ( "{}kaps/target/release/kaps" , QUARK_CONFIG_DIR ) )
214+ . arg ( format ! ( "{}/rootfs/usr/bin/kaps" , self . clone( ) . get_work_dir( ) ) )
215+ . output ( )
216+ . expect ( "failed to copy kaps" ) ;
217+
218+ self
86219 }
87220
88221 /// Generate the config file of the quardle
@@ -106,12 +239,13 @@ impl Quardle {
106239
107240 /// Create compressed archive from quardle files and result it at /out/<quardle_name>.qrk
108241 fn generate_archive ( & self ) -> Result < ( ) > {
109- let tar_gz = File :: create ( format ! ( "out/{}.qrk" , self . name) ) . unwrap ( ) ;
110- let enc = GzEncoder :: new ( tar_gz, Compression :: default ( ) ) ;
111- let mut tar = tar:: Builder :: new ( enc) ;
112- self . generate_config_file ( ) ?;
113- tar. append_dir_all ( "." , self . clone ( ) . get_work_dir ( ) ) . unwrap ( ) ;
114- Ok ( ( ) )
242+ let tar_gz = File :: create ( format ! ( "out/{}.qrk" , self . name) ) . unwrap ( ) ;
243+ let enc = GzEncoder :: new ( tar_gz, Compression :: default ( ) ) ;
244+ let mut tar = tar:: Builder :: new ( enc) ;
245+
246+ // tar.append_dir_all("./", self.clone().get_work_dir()).unwrap();
247+ tar. finish ( ) . unwrap ( ) ;
248+ Ok ( ( ) )
115249 }
116250}
117251
@@ -126,12 +260,6 @@ mod tests {
126260 assert_eq ! ( quardle. as_ref( ) . unwrap( ) . container_image_url, "container1" ) ;
127261 }
128262
129- #[ test]
130- fn quardle_delete ( ) {
131- let quardle = Quardle :: new ( "test" . to_string ( ) , "test" . to_string ( ) , false ) ;
132- assert_eq ! ( quardle. unwrap( ) . delete( ) . unwrap( ) , ( ) ) ;
133- }
134-
135263 #[ test]
136264 fn quardle_get_work_dir ( ) {
137265 let quardle = Quardle :: new ( "this-should-be-a-directory" . to_string ( ) , "my-container" . to_string ( ) , false ) ;
0 commit comments