Skip to content

Commit b6ba8d2

Browse files
authored
engineering: Implement select_servicing_type() (#374)
* broad strokes * progress * progress * fix comment * impl select_servicing_stage * nit * new * move back to context * make skip logs trace * enable runtime test * remove confusing comment * test runtime updates * remove newline * remove newline * remove outdated comment
1 parent d63afea commit b6ba8d2

File tree

12 files changed

+104
-68
lines changed

12 files changed

+104
-68
lines changed

.pipelines/templates/stages/testing_rollback/testing-template.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ parameters:
3232
displayName: Agent pool
3333
type: string
3434
default: "trident-ubuntu-1es-pool-eastus2"
35-
35+
3636
- name: skipExtensionTesting
3737
displayName: "Skip extension testing"
3838
type: string
@@ -46,7 +46,7 @@ parameters:
4646
- name: skipRuntimeUpdateTesting
4747
displayName: "Skip runtime update testing"
4848
type: string
49-
default: "true"
49+
default: "false"
5050

5151
jobs:
5252
- job: RollbackTesting_${{ replace(parameters.flavor, '-', '_') }}
@@ -115,7 +115,7 @@ jobs:
115115
116116
- bash: |
117117
set -eux
118-
118+
119119
STORM_DYNAMIC_FLAGS=""
120120
if [ "${{ parameters.verboseLogging }}" == "True" ]; then
121121
STORM_DYNAMIC_FLAGS="$STORM_DYNAMIC_FLAGS --verbose"

.pipelines/templates/stages/testing_rollback/vm-testing.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ stages:
119119
platform: qemu
120120
flavor: qemu-grub
121121
skipExtensionTesting: false
122+
skipRuntimeUpdateTesting: false
122123
- ${{ if eq(parameters.includeQemu, true) }}:
123124
- template: testing-template.yml
124125
parameters:
@@ -129,6 +130,7 @@ stages:
129130
# qemu image is grub + root verity. in this setup, Image Customizer cannot add
130131
# an extension to the servicing qcow2
131132
skipExtensionTesting: true
133+
skipRuntimeUpdateTesting: false
132134
- ${{ if eq(parameters.includeUKI, true) }}:
133135
- template: testing-template.yml
134136
parameters:
@@ -137,3 +139,4 @@ stages:
137139
platform: qemu
138140
flavor: uki
139141
skipExtensionTesting: false
142+
skipRuntimeUpdateTesting: false

crates/trident/src/subsystems/storage/image.rs renamed to crates/trident/src/engine/context/image.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,31 @@ use crate::engine::EngineContext;
1111
/// requests an OS image. If yes, update is needed, unless the old Host
1212
/// Configuration also requested an OS image and the URLs and SHA256 checksums
1313
/// are the same.
14-
pub(super) fn ab_update_required(ctx: &EngineContext) -> Result<bool, TridentError> {
15-
debug!("Checking OS image to determine if an A/B update is required");
16-
// Otherwise, continue checking OS images
17-
match (&ctx.spec_old.image, &ctx.spec.image) {
18-
// If OS image is not requested in the new spec, no update is needed.
19-
(None, None) => Ok(false),
20-
21-
(None, Some(_)) => {
22-
// This is most likely an offline-init's first update.
23-
Ok(true)
24-
}
25-
26-
// Update if the sha384 has changed (including if one is 'ignored'), or both are ignored but
27-
// the URL has changed.
28-
(Some(old_os_image), Some(new_os_image)) => Ok(old_os_image.sha384 != new_os_image.sha384
29-
|| old_os_image.sha384 == ImageSha384::Ignored && old_os_image.url != new_os_image.url),
30-
31-
(Some(_), None) => {
32-
// Return an error if the old spec requests an OS image but the new spec does not.
33-
Err(TridentError::new(InvalidInputError::from(
34-
HostConfigurationDynamicValidationError::DeployPartitionImagesAfterOsImage,
35-
)))
14+
impl EngineContext {
15+
pub(crate) fn ab_update_required(&self) -> Result<bool, TridentError> {
16+
debug!("Checking OS image to determine if an A/B update is required");
17+
match (&self.spec_old.image, &self.spec.image) {
18+
// If OS image is not requested in the new spec, no update is needed.
19+
(None, None) => Ok(false),
20+
21+
(None, Some(_)) => {
22+
// This is most likely an offline-init's first update.
23+
Ok(true)
24+
}
25+
26+
// Update if the sha384 has changed (including if one is 'ignored'), or both are ignored but
27+
// the URL has changed.
28+
(Some(old_os_image), Some(new_os_image)) => Ok(old_os_image.sha384
29+
!= new_os_image.sha384
30+
|| old_os_image.sha384 == ImageSha384::Ignored
31+
&& old_os_image.url != new_os_image.url),
32+
33+
(Some(_), None) => {
34+
// Return an error if the old spec requests an OS image but the new spec does not.
35+
Err(TridentError::new(InvalidInputError::from(
36+
HostConfigurationDynamicValidationError::DeployPartitionImagesAfterOsImage,
37+
)))
38+
}
3639
}
3740
}
3841
}
@@ -182,7 +185,7 @@ mod tests {
182185
image: Some(os_image),
183186
..Default::default()
184187
};
185-
assert!(!ab_update_required(&ctx).unwrap());
188+
assert!(!ctx.ab_update_required().unwrap());
186189

187190
// Test case #2: If OS image has changed, return true.
188191
let mut hc_os_image_updated = hc_os_image.clone();
@@ -192,6 +195,6 @@ mod tests {
192195
sha384: ImageSha384::Ignored,
193196
});
194197
ctx.spec = hc_os_image_updated;
195-
assert!(ab_update_required(&ctx).unwrap());
198+
assert!(ctx.ab_update_required().unwrap());
196199
}
197200
}

crates/trident/src/engine/context/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::osimage::OsImage;
2020

2121
#[allow(dead_code)]
2222
pub mod filesystem;
23-
23+
pub(crate) mod image;
2424
#[cfg(test)]
2525
mod test_utils;
2626

crates/trident/src/engine/mod.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
};
88

99
use chrono::Utc;
10-
use log::{debug, error, info, warn};
10+
use log::{debug, error, info, trace, warn};
1111

1212
use osutils::{dependencies::Dependency, path::join_relative};
1313
use trident_api::{
@@ -81,12 +81,10 @@ pub(crate) trait Subsystem: Send {
8181
// TODO: Implement dependencies
8282
// fn dependencies(&self) -> &'static [&'static str];
8383

84-
/// Select the servicing type based on the Host Status and Host Configuration.
85-
fn select_servicing_type(&self, ctx: &EngineContext) -> Result<ServicingType, TridentError> {
86-
if is_default(&ctx.spec_old) {
87-
return Ok(ServicingType::CleanInstall);
88-
}
89-
Ok(ServicingType::AbUpdate)
84+
/// Select the servicing type based on information in the Host Status
85+
/// relevant to a subsystem.
86+
fn select_servicing_type(&self, _ctx: &EngineContext) -> Result<ServicingType, TridentError> {
87+
Ok(ServicingType::NoActiveServicing)
9088
}
9189

9290
/// Servicing types on which a subsystem may run. By default, all subsystems
@@ -256,6 +254,28 @@ fn persist_background_log_and_metrics(
256254
}
257255
}
258256

257+
fn select_servicing_type(
258+
subsystems: &[Box<dyn Subsystem>],
259+
ctx: &EngineContext,
260+
) -> Result<ServicingType, TridentError> {
261+
if is_default(&ctx.spec_old) {
262+
return Ok(ServicingType::CleanInstall);
263+
} else if ctx
264+
.ab_update_required()
265+
.message("Failed to determine if A/B update is required")?
266+
{
267+
return Ok(ServicingType::AbUpdate);
268+
}
269+
270+
Ok(subsystems
271+
.iter()
272+
.map(|m| m.select_servicing_type(ctx))
273+
.collect::<Result<Vec<_>, TridentError>>()?
274+
.into_iter()
275+
.max()
276+
.unwrap_or(ServicingType::NoActiveServicing))
277+
}
278+
259279
#[tracing::instrument(skip_all)]
260280
fn validate_host_config(
261281
subsystems: &[Box<dyn Subsystem>],
@@ -273,7 +293,7 @@ fn validate_host_config(
273293
subsystem.name()
274294
))?;
275295
} else {
276-
debug!(
296+
trace!(
277297
"Skipping step 'Validate' for subsystem '{}'",
278298
subsystem.name()
279299
);
@@ -296,7 +316,7 @@ fn prepare(subsystems: &mut [Box<dyn Subsystem>], ctx: &EngineContext) -> Result
296316
subsystem.name()
297317
))?;
298318
} else {
299-
debug!(
319+
trace!(
300320
"Skipping step 'Prepare' for subsystem '{}'",
301321
subsystem.name()
302322
);
@@ -335,7 +355,7 @@ fn provision(
335355
subsystem.name()
336356
))?;
337357
} else {
338-
debug!(
358+
trace!(
339359
"Skipping step 'Provision' for subsystem '{}'",
340360
subsystem.name()
341361
);
@@ -377,7 +397,7 @@ fn configure(
377397
subsystem.name()
378398
))?;
379399
} else {
380-
debug!(
400+
trace!(
381401
"Skipping step 'Configure' for subsystem '{}'",
382402
subsystem.name()
383403
);
@@ -404,7 +424,7 @@ fn update_host_configuration(
404424
subsystem.name()
405425
))?;
406426
} else {
407-
debug!(
427+
trace!(
408428
"Skipping step 'Update Host Configuration' for subsystem '{}'",
409429
subsystem.name()
410430
);
@@ -427,7 +447,7 @@ fn clean_up(subsystems: &[Box<dyn Subsystem>], ctx: &EngineContext) -> Result<()
427447
subsystem.name()
428448
))?;
429449
} else {
430-
debug!(
450+
trace!(
431451
"Skipping step 'Clean Up' for subsystem '{}'",
432452
subsystem.name()
433453
);

crates/trident/src/engine/runtime_update.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use super::Subsystem;
2525
/// - ctx: EngineContext.
2626
/// - state: A mutable reference to the DataStore.
2727
/// - sender: Optional mutable reference to the gRPC sender.
28-
#[tracing::instrument(skip_all, fields(servicing_type = format!("{:?}", ctx.servicing_type)))]
28+
#[tracing::instrument(skip_all, fields(servicing_type = format!("{:?}", ServicingType::RuntimeUpdate)))]
2929
pub(crate) fn stage_update(
3030
subsystems: &mut [Box<dyn Subsystem>],
3131
ctx: EngineContext,
@@ -62,7 +62,6 @@ pub(crate) fn stage_update(
6262
// Update spec inside the Host Status with the new Host Configuration
6363
// (stored in ctx.spec).
6464
hs.spec = ctx.spec;
65-
// Update spec_old to the previous spec (ctx.spec_old == hs.spec)
6665
hs.spec_old = ctx.spec_old;
6766
})?;
6867
#[cfg(feature = "grpc-dangerous")]

crates/trident/src/engine/update.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,7 @@ pub(crate) fn update(
7272
}
7373

7474
debug!("Determining servicing type of required update");
75-
let servicing_type = subsystems
76-
.iter()
77-
.map(|m| m.select_servicing_type(&ctx))
78-
.collect::<Result<Vec<_>, TridentError>>()?
79-
.into_iter()
80-
.max()
81-
.unwrap_or(ServicingType::NoActiveServicing);
75+
let servicing_type = engine::select_servicing_type(&subsystems, &ctx)?;
8276
match servicing_type {
8377
ServicingType::NoActiveServicing => {
8478
info!("No update servicing required");

crates/trident/src/subsystems/extensions/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ impl Subsystem for ExtensionsSubsystem {
104104
RUNS_ON_ALL
105105
}
106106

107+
fn select_servicing_type(&self, ctx: &EngineContext) -> Result<ServicingType, TridentError> {
108+
if ctx.spec.os.sysexts != ctx.spec_old.os.sysexts
109+
|| ctx.spec.os.confexts != ctx.spec_old.os.confexts
110+
{
111+
return Ok(ServicingType::RuntimeUpdate);
112+
}
113+
Ok(ServicingType::NoActiveServicing)
114+
}
115+
107116
// prepare() is only called during runtime updates, so as to download the
108117
// extension files during Stage.
109118
fn prepare(&mut self, ctx: &EngineContext) -> Result<(), TridentError> {

crates/trident/src/subsystems/hooks.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use trident_api::{
2020
ROOT_MOUNT_POINT_PATH,
2121
},
2222
error::{InvalidInputError, ReportError, ServicingError, TridentError, TridentResultExt},
23+
is_default,
2324
status::ServicingType,
2425
};
2526

@@ -46,6 +47,13 @@ impl Subsystem for HooksSubsystem {
4647
self.writable_etc_overlay
4748
}
4849

50+
fn select_servicing_type(&self, ctx: &EngineContext) -> Result<ServicingType, TridentError> {
51+
if !is_default(&ctx.spec.scripts) {
52+
return Ok(ServicingType::AbUpdate);
53+
}
54+
Ok(ServicingType::NoActiveServicing)
55+
}
56+
4957
fn validate_host_config(&self, ctx: &EngineContext) -> Result<(), TridentError> {
5058
// Ensure that all scripts that should be run and have a path actually exist
5159
for script in ctx

crates/trident/src/subsystems/network.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ impl Subsystem for NetworkSubsystem {
2626
RUNS_ON_ALL
2727
}
2828

29+
fn select_servicing_type(&self, ctx: &EngineContext) -> Result<ServicingType, TridentError> {
30+
if ctx.spec.os.netplan != ctx.spec_old.os.netplan {
31+
return Ok(ServicingType::RuntimeUpdate);
32+
}
33+
Ok(ServicingType::NoActiveServicing)
34+
}
35+
2936
fn prepare(&mut self, ctx: &EngineContext) -> Result<(), TridentError> {
3037
if ctx.servicing_type == ServicingType::RuntimeUpdate
3138
&& ctx.spec.os.netplan != ctx.spec_old.os.netplan

0 commit comments

Comments
 (0)