Skip to content

Commit 64b420f

Browse files
committed
Add logic for mupdate override/artifacts for measurements
1 parent 79fac7d commit 64b420f

File tree

1 file changed

+115
-6
lines changed
  • sled-agent/measurements/src

1 file changed

+115
-6
lines changed

sled-agent/measurements/src/lib.rs

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ use sled_agent_config_reconciler::{ConfigReconcilerHandle, InventoryError};
1717
use sled_agent_resolvable_files::ZoneImageSourceResolver;
1818
use sled_agent_types::inventory::{
1919
ConfigReconcilerInventoryResult, OmicronSledConfig,
20-
SingleMeasurementInventory,
20+
SingleMeasurementInventory, OmicronSingleMeasurement,
21+
};
22+
use sled_agent_types::resolvable_files::{
23+
ManifestHashError, MupdateOverrideReadError,
2124
};
22-
use sled_agent_types::resolvable_files::ManifestHashError;
2325
use slog::Logger;
24-
use slog::error;
26+
use slog::{error, info, warn};
2527
use slog_error_chain::InlineErrorChain;
2628
use slog_error_chain::SlogInlineError;
29+
use std::collections::BTreeSet;
2730
use std::sync::Arc;
2831
use thiserror::Error;
2932
use tokio::sync::oneshot;
@@ -42,6 +45,8 @@ pub enum MeasurementError {
4245
ManifestHashError(#[source] ManifestHashError),
4346
#[error("Error notification from ledger start")]
4447
LedgerStartFailure,
48+
#[error("mupdate override error")]
49+
MupdateOverrideError(#[source] MupdateOverrideReadError),
4550
}
4651

4752
#[derive(Clone)]
@@ -191,10 +196,114 @@ impl MeasurementsHandle {
191196
}
192197
}
193198

194-
// Right now we only take into account the install dataset/MUPdate case.
195-
// Future work will also give artifact hashes.
196199
fn measurements_from_sled_config(
197-
_config: &OmicronSledConfig,
200+
config: &OmicronSledConfig,
201+
log: &Logger,
202+
config_reconciler: &Arc<ConfigReconcilerHandle>,
203+
zone_image_resolver: &ZoneImageSourceResolver,
204+
) -> Result<Vec<Utf8PathBuf>, MeasurementError> {
205+
let status = zone_image_resolver.status();
206+
let use_install = config.measurements.is_empty();
207+
208+
match (&status.mupdate_override.boot_disk_override, use_install) {
209+
// We are set to use the install dataset and there is a MUPdate override
210+
(Ok(Some(override_info)), true) => {
211+
info!(log, "mupdate override active, already using install dataset";
212+
"mupdate_override_id" => %override_info.mupdate_uuid);
213+
measurements_from_install_dataset(
214+
log,
215+
config_reconciler,
216+
zone_image_resolver,
217+
)
218+
}
219+
// Actual override case! Use the install dataset
220+
(Ok(Some(override_info)), false) => {
221+
info!(log, "mupdate override active, redirecting to install \
222+
dataset from artifacts";
223+
"mupdate_override_id" => %override_info.mupdate_uuid);
224+
measurements_from_install_dataset(
225+
log,
226+
config_reconciler,
227+
zone_image_resolver,
228+
)
229+
}
230+
// No override is active but we're still using the install dataset
231+
(Ok(None), true) => measurements_from_install_dataset(
232+
log,
233+
config_reconciler,
234+
zone_image_resolver,
235+
),
236+
// No override and we're not using the install dataset
237+
(Ok(None), false) => measurements_from_artifact_dataset(
238+
config_reconciler,
239+
&config.measurements,
240+
),
241+
// Error getting the mupdate override but we're still set to use
242+
// the install dataset
243+
(Err(error), true) => {
244+
warn!(log, "error obtaining mupdate override but we're still \
245+
using install dataset, proceeding with caution";
246+
"error" => InlineErrorChain::new(error));
247+
measurements_from_install_dataset(
248+
log,
249+
config_reconciler,
250+
zone_image_resolver,
251+
)
252+
}
253+
// Error getting the mupdate override but we're supposed to
254+
// use the artifacts
255+
(Err(error), false) => {
256+
error!(log, "error obtaining mupdate override, can't use the \
257+
artifacts, returning an error";
258+
"error" => InlineErrorChain::new(error));
259+
260+
Err(MeasurementError::MupdateOverrideError(error.clone()))
261+
}
262+
}
263+
}
264+
265+
fn measurements_from_artifact_dataset(
266+
config_reconciler: &Arc<ConfigReconcilerHandle>,
267+
measurements: &BTreeSet<OmicronSingleMeasurement>,
268+
) -> Result<Vec<Utf8PathBuf>, MeasurementError> {
269+
let mut valid = Vec::new();
270+
let mut errors = Vec::new();
271+
272+
for m in measurements {
273+
// We have mulitple possible artifact paths. We only need one per hash
274+
let mut found = false;
275+
for dataset in config_reconciler
276+
.internal_disks_rx()
277+
.current()
278+
.all_artifact_datasets()
279+
{
280+
let potential_path = dataset.join(m.hash.to_string());
281+
282+
// This could technically be a TOCTOU issue but arguably all the paths we
283+
// return have the same potential issue and hopefully the callers handle
284+
// the failure gracefully
285+
if potential_path.is_file() {
286+
found = true;
287+
valid.push(potential_path);
288+
break;
289+
}
290+
}
291+
if !found {
292+
errors.push(format!(
293+
"artifact {} missing from all artifact datasets",
294+
m.hash
295+
));
296+
}
297+
}
298+
299+
if errors.is_empty() {
300+
Ok(valid)
301+
} else {
302+
Err(MeasurementError::Paths { valid, errors })
303+
}
304+
}
305+
306+
fn measurements_from_install_dataset(
198307
log: &Logger,
199308
config_reconciler: &Arc<ConfigReconcilerHandle>,
200309
zone_image_resolver: &ZoneImageSourceResolver,

0 commit comments

Comments
 (0)