44
55use std:: convert:: TryFrom ;
66
7+ use log:: warn;
78use oci_distribution:: Reference ;
9+ use tempfile:: NamedTempFile ;
810
911use crate :: image:: ImageDetails ;
1012use crate :: storage:: OciStorage ;
1113use crate :: { EnclaveBuildError , Result } ;
1214
15+ /// Trait which provides an interface for handling an image (OCI or Docker)
16+ pub trait ImageManager {
17+ fn image_name ( & self ) -> & str ;
18+ /// Inspects the image and returns its metadata in the form of a JSON Value
19+ fn inspect_image ( & self ) -> Result < serde_json:: Value > ;
20+ /// Returns the architecture of the image
21+ fn architecture ( & self ) -> Result < String > ;
22+ /// Returns two temp files containing the CMD and ENV expressions extracted from the image
23+ fn extract_expressions ( & self ) -> Result < ( NamedTempFile , NamedTempFile ) > ;
24+ }
25+
1326pub struct OciImageManager {
1427 /// Name of the container image.
1528 image_name : String ,
@@ -25,16 +38,15 @@ impl OciImageManager {
2538 let image_name = normalize_tag ( image_name) ?;
2639
2740 // The docker daemon is not used, so a local storage needs to be created
28- let storage =
29- match OciStorage :: get_default_root_path ( ) . map_err ( |err| eprintln ! ( "{:?}" , err) ) {
30- Ok ( root_path) => {
31- // Try to create/read the storage. If the storage could not be created, log the error
32- OciStorage :: new ( & root_path)
33- . map_err ( |err| eprintln ! ( "{:?}" , err) )
34- . ok ( )
35- }
36- Err ( _) => None ,
37- } ;
41+ let storage = match OciStorage :: get_default_root_path ( ) . map_err ( |err| warn ! ( "{:?}" , err) ) {
42+ Ok ( root_path) => {
43+ // Try to create/read the storage. If the storage could not be created, log the error
44+ OciStorage :: new ( & root_path)
45+ . map_err ( |err| warn ! ( "{:?}" , err) )
46+ . ok ( )
47+ }
48+ Err ( _) => None ,
49+ } ;
3850
3951 let image_details = Self :: fetch_image_details ( & image_name, storage) . await ?;
4052
@@ -60,11 +72,7 @@ impl OciImageManager {
6072
6173 let image_details = if let Some ( storage) = local_storage {
6274 // Try to fetch the image from the storage
63- storage. fetch_image_details ( image_name) . map_err ( |err| {
64- // Log the fetching error
65- eprintln ! ( "{:?}" , err) ;
66- err
67- } )
75+ storage. fetch_image_details ( image_name)
6876 } else {
6977 Err ( EnclaveBuildError :: OciStorageNotFound (
7078 "Local storage missing" . to_string ( ) ,
@@ -74,15 +82,16 @@ impl OciImageManager {
7482 // If the fetching failed, pull it from remote and store it
7583 match image_details {
7684 Ok ( details) => Ok ( details) ,
77- Err ( _) => {
85+ Err ( err) => {
86+ warn ! ( "Fetching from storage failed: {}" , err) ;
7887 // The image is not stored, so try to pull and then store it
7988 let image_data = crate :: pull:: pull_image_data ( image_name) . await ?;
8089
8190 // If the store operation fails, discard error and proceed with getting the details
8291 if let Some ( local_storage) = storage. as_mut ( ) {
8392 local_storage
8493 . store_image_data ( image_name, & image_data)
85- . map_err ( |err| eprintln ! ( "Failed to store image: {:?}" , err) )
94+ . map_err ( |err| warn ! ( "Failed to store image: {:?}" , err) )
8695 . ok ( ) ;
8796 }
8897
@@ -105,8 +114,35 @@ fn normalize_tag(image_name: &str) -> Result<String> {
105114 }
106115}
107116
117+ impl ImageManager for OciImageManager {
118+ fn image_name ( & self ) -> & str {
119+ & self . image_name
120+ }
121+
122+ /// Inspect the image and return its description as a JSON String.
123+ fn inspect_image ( & self ) -> Result < serde_json:: Value > {
124+ // Serialize to a serde_json::Value
125+ serde_json:: to_value ( & self . image_details ) . map_err ( EnclaveBuildError :: SerdeError )
126+ }
127+
128+ /// Extracts the CMD and ENV expressions from the image and returns them each in a
129+ /// temporary file
130+ fn extract_expressions ( & self ) -> Result < ( NamedTempFile , NamedTempFile ) > {
131+ self . image_details . extract_expressions ( )
132+ }
133+
134+ /// Returns architecture information of the image.
135+ fn architecture ( & self ) -> Result < String > {
136+ Ok ( format ! ( "{}" , self . image_details. config( ) . architecture( ) ) )
137+ }
138+ }
139+
108140#[ cfg( test) ]
109- pub mod tests {
141+ mod tests {
142+ use super :: * ;
143+ use std:: fs:: File ;
144+ use std:: io:: Read ;
145+
110146 use sha2:: Digest ;
111147
112148 use super :: { normalize_tag, OciImageManager } ;
@@ -177,4 +213,37 @@ pub mod tests {
177213
178214 assert_eq ! ( & config_hash, IMAGE_HASH ) ;
179215 }
216+
217+ /// Test extracted configuration is as expected
218+ #[ tokio:: test]
219+ async fn test_config ( ) {
220+ #[ cfg( target_arch = "x86_64" ) ]
221+ let image_manager = OciImageManager :: new (
222+ "667861386598.dkr.ecr.us-east-1.amazonaws.com/enclaves-samples:vsock-sample-server-x86_64" ,
223+ ) . await . unwrap ( ) ;
224+ #[ cfg( target_arch = "aarch64" ) ]
225+ let mut image_manager = OciImageManager :: new (
226+ "667861386598.dkr.ecr.us-east-1.amazonaws.com/enclaves-samples:vsock-sample-server-aarch64" ,
227+ ) . await . unwrap ( ) ;
228+
229+ let ( cmd_file, env_file) = image_manager. extract_expressions ( ) . unwrap ( ) ;
230+ let mut cmd_file = File :: open ( cmd_file. path ( ) ) . unwrap ( ) ;
231+ let mut env_file = File :: open ( env_file. path ( ) ) . unwrap ( ) ;
232+
233+ let mut cmd = String :: new ( ) ;
234+ cmd_file. read_to_string ( & mut cmd) . unwrap ( ) ;
235+ assert_eq ! (
236+ cmd,
237+ "/bin/sh\n \
238+ -c\n \
239+ ./vsock-sample server --port 5005\n "
240+ ) ;
241+
242+ let mut env = String :: new ( ) ;
243+ env_file. read_to_string ( & mut env) . unwrap ( ) ;
244+ assert_eq ! (
245+ env,
246+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n "
247+ ) ;
248+ }
180249}
0 commit comments