@@ -20,28 +20,30 @@ use std::collections::BTreeMap;
2020use std:: fs:: File ;
2121use std:: io:: { BufReader , Cursor , Read , Seek , SeekFrom , Write } ;
2222use std:: time:: Duration ;
23- use tokio:: sync:: Mutex ;
23+ use tokio:: sync:: { Mutex , MutexGuard } ;
2424use tracing:: { debug, info, warn} ;
2525
2626#[ derive( Debug ) ]
2727struct StoredArtifact {
2828 description : super :: Artifact ,
29- cached_path : Option < Utf8PathBuf > ,
29+ cached_path : Mutex < Option < Utf8PathBuf > > ,
3030}
3131
3232impl StoredArtifact {
3333 fn new ( description : super :: Artifact ) -> Self {
34- Self { description, cached_path : None }
34+ Self { description, cached_path : Mutex :: new ( None ) }
3535 }
3636
3737 async fn ensure (
38- & mut self ,
38+ & self ,
3939 local_dir : & Utf8Path ,
4040 downloader : & DownloadConfig ,
4141 ) -> anyhow:: Result < Utf8PathBuf > {
42+ let mut path_guard = self . cached_path . lock ( ) . await ;
43+
4244 // If the artifact already exists and has been verified, return the path
4345 // to it straightaway.
44- if let Some ( path) = & self . cached_path {
46+ if let Some ( path) = path_guard . as_ref ( ) {
4547 debug ! ( %path, "Verified artifact already exists" ) ;
4648 return Ok ( path. clone ( ) ) ;
4749 }
@@ -67,7 +69,7 @@ impl StoredArtifact {
6769 // The file is in the right place and has the right hash (if that
6870 // was checked), so mark it as cached and return the cached path.
6971 debug ! ( %path, "Locally-sourced artifact is valid, caching its path" ) ;
70- self . cached_path = Some ( path. clone ( ) ) ;
72+ * path_guard = Some ( path. clone ( ) ) ;
7173 return Ok ( path. clone ( ) ) ;
7274 }
7375
@@ -91,7 +93,7 @@ impl StoredArtifact {
9193 if file_hash_equals ( & maybe_path, expected_digest) . is_ok ( ) {
9294 debug ! ( %maybe_path,
9395 "Valid artifact already exists, caching its path" ) ;
94- return self . cache_path ( maybe_path) ;
96+ return self . cache_path ( path_guard , maybe_path) ;
9597 } else {
9698 warn ! ( %maybe_path, "Existing artifact is invalid, deleting it" ) ;
9799 std:: fs:: remove_file ( & maybe_path) ?;
@@ -140,11 +142,12 @@ impl StoredArtifact {
140142 permissions. set_readonly ( true ) ;
141143 new_file. set_permissions ( permissions) ?;
142144
143- self . cache_path ( maybe_path)
145+ self . cache_path ( path_guard , maybe_path)
144146 }
145147
146148 fn cache_path (
147- & mut self ,
149+ & self ,
150+ mut path_guard : MutexGuard < ' _ , Option < Utf8PathBuf > > ,
148151 mut path : Utf8PathBuf ,
149152 ) -> anyhow:: Result < Utf8PathBuf > {
150153 if let Some ( ref untar_path) = self . description . untar {
@@ -166,15 +169,15 @@ impl StoredArtifact {
166169 }
167170 } ;
168171
169- self . cached_path = Some ( path. clone ( ) ) ;
172+ * path_guard = Some ( path. clone ( ) ) ;
170173 Ok ( path)
171174 }
172175}
173176
174177#[ derive( Debug ) ]
175178pub struct Store {
176179 local_dir : Utf8PathBuf ,
177- artifacts : BTreeMap < String , Mutex < StoredArtifact > > ,
180+ artifacts : BTreeMap < String , StoredArtifact > ,
178181 downloader : DownloadConfig ,
179182}
180183
@@ -199,7 +202,7 @@ impl Store {
199202 let Manifest { artifacts, remote_server_uris } = manifest;
200203 let artifacts = artifacts
201204 . into_iter ( )
202- . map ( |( k, v) | ( k, Mutex :: new ( StoredArtifact :: new ( v) ) ) )
205+ . map ( |( k, v) | ( k, StoredArtifact :: new ( v) ) )
203206 . collect ( ) ;
204207
205208 let buildomat_backoff = backoff:: ExponentialBackoffBuilder :: new ( )
@@ -281,7 +284,7 @@ impl Store {
281284
282285 let _old = self . artifacts . insert (
283286 BASE_PROPOLIS_ARTIFACT . to_string ( ) ,
284- Mutex :: new ( StoredArtifact :: new ( artifact) ) ,
287+ StoredArtifact :: new ( artifact) ,
285288 ) ;
286289 assert ! ( _old. is_none( ) ) ;
287290 Ok ( ( ) )
@@ -335,24 +338,36 @@ impl Store {
335338
336339 let _old = self . artifacts . insert (
337340 CRUCIBLE_DOWNSTAIRS_ARTIFACT . to_string ( ) ,
338- Mutex :: new ( StoredArtifact :: new ( artifact) ) ,
341+ StoredArtifact :: new ( artifact) ,
339342 ) ;
340343 assert ! ( _old. is_none( ) ) ;
341344 Ok ( ( ) )
342345 }
343346 }
344347 }
345348
349+ pub fn get_guest_os_image_kind (
350+ & self ,
351+ artifact_name : & str ,
352+ ) -> anyhow:: Result < GuestOsKind > {
353+ let entry = self . get_artifact ( artifact_name) ?;
354+ match entry. description . kind {
355+ super :: ArtifactKind :: GuestOs ( kind) => Ok ( kind) ,
356+ _ => Err ( anyhow:: anyhow!(
357+ "artifact {artifact_name} is not a guest OS image"
358+ ) ) ,
359+ }
360+ }
361+
346362 pub async fn get_guest_os_image (
347363 & self ,
348364 artifact_name : & str ,
349365 ) -> anyhow:: Result < ( Utf8PathBuf , GuestOsKind ) > {
350366 let entry = self . get_artifact ( artifact_name) ?;
351- let mut guard = entry. lock ( ) . await ;
352- match guard. description . kind {
367+ match entry. description . kind {
353368 super :: ArtifactKind :: GuestOs ( kind) => {
354369 let path =
355- guard . ensure ( & self . local_dir , & self . downloader ) . await ?;
370+ entry . ensure ( & self . local_dir , & self . downloader ) . await ?;
356371 Ok ( ( path, kind) )
357372 }
358373 _ => Err ( anyhow:: anyhow!(
@@ -366,10 +381,9 @@ impl Store {
366381 artifact_name : & str ,
367382 ) -> anyhow:: Result < Utf8PathBuf > {
368383 let entry = self . get_artifact ( artifact_name) ?;
369- let mut guard = entry. lock ( ) . await ;
370- match guard. description . kind {
384+ match entry. description . kind {
371385 super :: ArtifactKind :: Bootrom => {
372- guard . ensure ( & self . local_dir , & self . downloader ) . await
386+ entry . ensure ( & self . local_dir , & self . downloader ) . await
373387 }
374388 _ => Err ( anyhow:: anyhow!(
375389 "artifact {artifact_name} is not a bootrom"
@@ -382,10 +396,9 @@ impl Store {
382396 artifact_name : & str ,
383397 ) -> anyhow:: Result < Utf8PathBuf > {
384398 let entry = self . get_artifact ( artifact_name) ?;
385- let mut guard = entry. lock ( ) . await ;
386- match guard. description . kind {
399+ match entry. description . kind {
387400 super :: ArtifactKind :: PropolisServer => {
388- guard . ensure ( & self . local_dir , & self . downloader ) . await
401+ entry . ensure ( & self . local_dir , & self . downloader ) . await
389402 }
390403 _ => Err ( anyhow:: anyhow!(
391404 "artifact {artifact_name} is not a Propolis server"
@@ -395,21 +408,17 @@ impl Store {
395408
396409 pub async fn get_crucible_downstairs ( & self ) -> anyhow:: Result < Utf8PathBuf > {
397410 let entry = self . get_artifact ( CRUCIBLE_DOWNSTAIRS_ARTIFACT ) ?;
398- let mut guard = entry. lock ( ) . await ;
399- match guard. description . kind {
411+ match entry. description . kind {
400412 super :: ArtifactKind :: CrucibleDownstairs => {
401- guard . ensure ( & self . local_dir , & self . downloader ) . await
413+ entry . ensure ( & self . local_dir , & self . downloader ) . await
402414 }
403415 _ => Err ( anyhow:: anyhow!(
404416 "artifact {CRUCIBLE_DOWNSTAIRS_ARTIFACT} is not a Crucible downstairs binary" ,
405417 ) ) ,
406418 }
407419 }
408420
409- fn get_artifact (
410- & self ,
411- name : & str ,
412- ) -> anyhow:: Result < & Mutex < StoredArtifact > > {
421+ fn get_artifact ( & self , name : & str ) -> anyhow:: Result < & StoredArtifact > {
413422 self . artifacts . get ( name) . ok_or_else ( || {
414423 anyhow:: anyhow!( "artifact {name} not found in store" )
415424 } )
@@ -449,10 +458,9 @@ impl Store {
449458 untar : None ,
450459 } ;
451460
452- let _old: Option < Mutex < StoredArtifact > > = self . artifacts . insert (
453- artifact_name. to_string ( ) ,
454- Mutex :: new ( StoredArtifact :: new ( artifact) ) ,
455- ) ;
461+ let _old: Option < StoredArtifact > = self
462+ . artifacts
463+ . insert ( artifact_name. to_string ( ) , StoredArtifact :: new ( artifact) ) ;
456464 assert ! ( _old. is_none( ) ) ;
457465
458466 Ok ( ( ) )
0 commit comments