Skip to content

Commit d3826c5

Browse files
authored
[3/n] [sled-agent] put zone image resolver behind Arc/Mutex (#8080)
The `ZoneImageSourceResolver` is going to gain the ability to reset the mupdate override file, which involves synchronizing on-disk and in-memory state. It's easiest to ensure that accesses to the resolver are serialized. We're also going to have to clone this resolver in the future, so use `Arc`. Since there's no async involved here, it's simpler to do this than use the actor model.
1 parent d0c601d commit d3826c5

File tree

1 file changed

+42
-5
lines changed

1 file changed

+42
-5
lines changed

sled-agent/zone-images/src/source_resolver.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use nexus_sled_agent_shared::inventory::OmicronZoneImageSource;
99
use sled_storage::dataset::INSTALL_DATASET;
1010
use sled_storage::dataset::M2_ARTIFACT_DATASET;
1111
use sled_storage::resources::AllDisks;
12-
use std::sync::OnceLock;
12+
use std::sync::Arc;
13+
use std::sync::Mutex;
1314

1415
/// Places to look for an Omicron zone image.
1516
pub struct ZoneImageFileSource {
@@ -27,20 +28,26 @@ pub struct ZoneImageFileSource {
2728

2829
/// Resolves [`OmicronZoneImageSource`] instances into file names and search
2930
/// paths.
31+
///
32+
/// This is cheaply cloneable.
33+
#[derive(Clone)]
3034
pub struct ZoneImageSourceResolver {
31-
image_directory_override: OnceLock<Utf8PathBuf>,
35+
// Inner state, guarded by a mutex.
36+
inner: Arc<Mutex<ResolverInner>>,
3237
}
3338

3439
impl ZoneImageSourceResolver {
3540
pub fn new() -> Self {
36-
ZoneImageSourceResolver { image_directory_override: OnceLock::new() }
41+
ZoneImageSourceResolver {
42+
inner: Arc::new(Mutex::new(ResolverInner::new())),
43+
}
3744
}
3845

3946
/// Overrides the image directory with another one.
4047
///
4148
/// Intended for testing.
4249
pub fn override_image_directory(&self, path: Utf8PathBuf) {
43-
self.image_directory_override.set(path).unwrap();
50+
self.inner.lock().unwrap().override_image_directory(path);
4451
}
4552

4653
/// Returns a [`ZoneImageFileSource`] consisting of the file name, plus a
@@ -49,6 +56,36 @@ impl ZoneImageSourceResolver {
4956
&self,
5057
image_source: &OmicronZoneImageSource,
5158
all_disks: &AllDisks,
59+
) -> ZoneImageFileSource {
60+
let inner = self.inner.lock().unwrap();
61+
inner.file_source_for(image_source, all_disks)
62+
}
63+
}
64+
65+
#[derive(Debug)]
66+
struct ResolverInner {
67+
image_directory_override: Option<Utf8PathBuf>,
68+
}
69+
70+
impl ResolverInner {
71+
fn new() -> Self {
72+
Self { image_directory_override: None }
73+
}
74+
75+
fn override_image_directory(
76+
&mut self,
77+
image_directory_override: Utf8PathBuf,
78+
) {
79+
if let Some(dir) = &self.image_directory_override {
80+
panic!("image_directory_override already set to {dir}");
81+
}
82+
self.image_directory_override = Some(image_directory_override);
83+
}
84+
85+
fn file_source_for(
86+
&self,
87+
image_source: &OmicronZoneImageSource,
88+
all_disks: &AllDisks,
5289
) -> ZoneImageFileSource {
5390
let file_name = match image_source {
5491
OmicronZoneImageSource::InstallDataset => {
@@ -64,7 +101,7 @@ impl ZoneImageSourceResolver {
64101
let mut zone_image_paths =
65102
vec![Utf8PathBuf::from("/opt/oxide")];
66103
// Inject an image path if requested by a test.
67-
if let Some(path) = self.image_directory_override.get() {
104+
if let Some(path) = &self.image_directory_override {
68105
zone_image_paths.push(path.clone());
69106
};
70107

0 commit comments

Comments
 (0)