Skip to content

Commit a29f08b

Browse files
authored
[wicket] mark skipped components as SKIPPED (#3717)
Just check that the "SpComponentUpdate" step was skipped. To test this, I had to add two facilities: 1. Detecting and storing the currently active slot in sp-sim. 2. New environment variables for simulating SP and RoT update results. Closes #3641.
1 parent bf69cf6 commit a29f08b

File tree

12 files changed

+249
-11
lines changed

12 files changed

+249
-11
lines changed

openapi/wicketd.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,6 +2527,24 @@
25272527
}
25282528
]
25292529
},
2530+
"test_simulate_rot_result": {
2531+
"nullable": true,
2532+
"description": "If passed in, simulates a result for the RoT update.\n\nThis is used for testing.",
2533+
"allOf": [
2534+
{
2535+
"$ref": "#/components/schemas/UpdateSimulatedResult"
2536+
}
2537+
]
2538+
},
2539+
"test_simulate_sp_result": {
2540+
"nullable": true,
2541+
"description": "If passed in, simulates a result for the SP update.\n\nThis is used for testing.",
2542+
"allOf": [
2543+
{
2544+
"$ref": "#/components/schemas/UpdateSimulatedResult"
2545+
}
2546+
]
2547+
},
25302548
"test_step_seconds": {
25312549
"nullable": true,
25322550
"description": "If passed in, creates a test step that lasts these many seconds long.\n\nThis is used for testing.",
@@ -3990,6 +4008,16 @@
39904008
}
39914009
]
39924010
},
4011+
"UpdateSimulatedResult": {
4012+
"description": "A simulated result for a component update.\n\nUsed by [`StartUpdateOptions`].",
4013+
"type": "string",
4014+
"enum": [
4015+
"success",
4016+
"warning",
4017+
"skipped",
4018+
"failure"
4019+
]
4020+
},
39934021
"UpdateStepId": {
39944022
"oneOf": [
39954023
{

sp-sim/src/gimlet.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use crate::config::GimletConfig;
66
use crate::config::SpComponentConfig;
7+
use crate::helpers::rot_slot_id_from_u16;
8+
use crate::helpers::rot_slot_id_to_u16;
79
use crate::rot::RotSprocketExt;
810
use crate::serial_number_padded;
911
use crate::server;
@@ -494,6 +496,7 @@ struct Handler {
494496

495497
attached_mgs: Arc<Mutex<Option<(SpComponent, SpPort, SocketAddrV6)>>>,
496498
incoming_serial_console: HashMap<SpComponent, UnboundedSender<Vec<u8>>>,
499+
rot_active_slot: RotSlotId,
497500
power_state: PowerState,
498501
startup_options: StartupOptions,
499502
update_state: UpdateState,
@@ -527,6 +530,7 @@ impl Handler {
527530
serial_number,
528531
attached_mgs,
529532
incoming_serial_console,
533+
rot_active_slot: RotSlotId::A,
530534
power_state: PowerState::A2,
531535
startup_options: StartupOptions::empty(),
532536
update_state: UpdateState::NotPrepared,
@@ -1107,12 +1111,18 @@ impl SpHandler for Handler {
11071111
component: SpComponent,
11081112
) -> Result<u16, SpError> {
11091113
warn!(
1110-
&self.log, "asked for component active slot (not supported for sim components)";
1114+
&self.log, "asked for component active slot";
11111115
"sender" => %sender,
11121116
"port" => ?port,
11131117
"component" => ?component,
11141118
);
1115-
Err(SpError::RequestUnsupportedForComponent)
1119+
if component == SpComponent::ROT {
1120+
Ok(rot_slot_id_to_u16(self.rot_active_slot))
1121+
} else {
1122+
// The real SP returns `RequestUnsupportedForComponent` for anything
1123+
// other than the RoT, including SP_ITSELF.
1124+
Err(SpError::RequestUnsupportedForComponent)
1125+
}
11161126
}
11171127

11181128
fn component_set_active_slot(
@@ -1124,14 +1134,21 @@ impl SpHandler for Handler {
11241134
persist: bool,
11251135
) -> Result<(), SpError> {
11261136
warn!(
1127-
&self.log, "asked to set component active slot (not supported for sim components)";
1137+
&self.log, "asked to set component active slot";
11281138
"sender" => %sender,
11291139
"port" => ?port,
11301140
"component" => ?component,
11311141
"slot" => slot,
11321142
"persist" => persist,
11331143
);
1134-
Err(SpError::RequestUnsupportedForComponent)
1144+
if component == SpComponent::ROT {
1145+
self.rot_active_slot = rot_slot_id_from_u16(slot)?;
1146+
Ok(())
1147+
} else {
1148+
// The real SP returns `RequestUnsupportedForComponent` for anything
1149+
// other than the RoT, including SP_ITSELF.
1150+
Err(SpError::RequestUnsupportedForComponent)
1151+
}
11351152
}
11361153

11371154
fn component_action(

sp-sim/src/helpers.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use gateway_messages::{RotSlotId, SpError};
6+
7+
pub(crate) fn rot_slot_id_to_u16(slot_id: RotSlotId) -> u16 {
8+
match slot_id {
9+
RotSlotId::A => 0,
10+
RotSlotId::B => 1,
11+
}
12+
}
13+
14+
pub(crate) fn rot_slot_id_from_u16(slot_id: u16) -> Result<RotSlotId, SpError> {
15+
match slot_id {
16+
0 => Ok(RotSlotId::A),
17+
1 => Ok(RotSlotId::B),
18+
_ => Err(SpError::InvalidSlotForComponent),
19+
}
20+
}

sp-sim/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
pub mod config;
66
mod gimlet;
7+
mod helpers;
78
mod rot;
89
mod server;
910
mod sidecar;

sp-sim/src/sidecar.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::config::Config;
66
use crate::config::SidecarConfig;
77
use crate::config::SimulatedSpsConfig;
88
use crate::config::SpComponentConfig;
9+
use crate::helpers::rot_slot_id_from_u16;
10+
use crate::helpers::rot_slot_id_to_u16;
911
use crate::rot::RotSprocketExt;
1012
use crate::serial_number_padded;
1113
use crate::server;
@@ -297,6 +299,7 @@ struct Handler {
297299

298300
serial_number: String,
299301
ignition: FakeIgnition,
302+
rot_active_slot: RotSlotId,
300303
power_state: PowerState,
301304
}
302305

@@ -326,6 +329,7 @@ impl Handler {
326329
leaked_component_description_strings,
327330
serial_number,
328331
ignition,
332+
rot_active_slot: RotSlotId::A,
329333
power_state: PowerState::A2,
330334
}
331335
}
@@ -828,12 +832,18 @@ impl SpHandler for Handler {
828832
component: SpComponent,
829833
) -> Result<u16, SpError> {
830834
warn!(
831-
&self.log, "asked for component active slot (not supported for sim components)";
835+
&self.log, "asked for component active slot";
832836
"sender" => %sender,
833837
"port" => ?port,
834838
"component" => ?component,
835839
);
836-
Err(SpError::RequestUnsupportedForComponent)
840+
if component == SpComponent::ROT {
841+
Ok(rot_slot_id_to_u16(self.rot_active_slot))
842+
} else {
843+
// The real SP returns `RequestUnsupportedForComponent` for anything
844+
// other than the RoT, including SP_ITSELF.
845+
Err(SpError::RequestUnsupportedForComponent)
846+
}
837847
}
838848

839849
fn component_set_active_slot(
@@ -845,14 +855,21 @@ impl SpHandler for Handler {
845855
persist: bool,
846856
) -> Result<(), SpError> {
847857
warn!(
848-
&self.log, "asked to set component active slot (not supported for sim components)";
858+
&self.log, "asked to set component active slot";
849859
"sender" => %sender,
850860
"port" => ?port,
851861
"component" => ?component,
852862
"slot" => slot,
853863
"persist" => persist,
854864
);
855-
Err(SpError::RequestUnsupportedForComponent)
865+
if component == SpComponent::ROT {
866+
self.rot_active_slot = rot_slot_id_from_u16(slot)?;
867+
Ok(())
868+
} else {
869+
// The real SP returns `RequestUnsupportedForComponent` for anything
870+
// other than the RoT, including SP_ITSELF.
871+
Err(SpError::RequestUnsupportedForComponent)
872+
}
856873
}
857874

858875
fn component_action(

update-engine/src/events.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,23 @@ impl<S: StepSpec> StepOutcome<S> {
955955
}
956956
}
957957
}
958+
959+
/// Returns true if the step was successful, including success with
960+
/// warning.
961+
pub fn is_success_or_warning(&self) -> bool {
962+
match self {
963+
Self::Success { .. } | Self::Warning { .. } => true,
964+
Self::Skipped { .. } => false,
965+
}
966+
}
967+
968+
/// Returns true if the step was skipped.
969+
pub fn is_skipped(&self) -> bool {
970+
match self {
971+
Self::Skipped { .. } => true,
972+
Self::Success { .. } | Self::Warning { .. } => false,
973+
}
974+
}
958975
}
959976

960977
#[derive(Deserialize, Serialize, JsonSchema)]

wicket-common/src/update_events.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ pub enum UpdateTerminalError {
146146
#[from]
147147
error: NestedEngineError<TestStepSpec>,
148148
},
149+
#[error("simulated failure result")]
150+
SimulatedFailure,
149151
#[error("error updating component")]
150152
ComponentNestedError {
151153
#[from]

wicket/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,34 @@ an appropriate value. For example:
136136
WICKET_UPDATE_TEST_STEP_SECONDS=15 cargo run --bin wicket
137137
```
138138

139+
## Adding simulated results to individual steps
140+
141+
Some individual steps support having simulated results via environment variables.
142+
143+
Environment variables supported are:
144+
145+
* `WICKET_UPDATE_TEST_SIMULATE_ROT_RESULT`: Simulates a result for the "Updating RoT" step.
146+
* `WICKET_UPDATE_TEST_SIMULATE_SP_RESULT`: Simulates a result for the "Updating SP" step.
147+
148+
The environment variable can be set to:
149+
150+
* `success`: A success outcome.
151+
* `warning`: Success with warning.
152+
* `failure`: A failure.
153+
* `skipped`: A skipped outcome.
154+
155+
### Example
156+
157+
If wicket is invoked as:
158+
159+
```
160+
WICKET_UPDATE_TEST_SIMULATE_ROT_RESULT=skipped cargo run --bin wicket
161+
```
162+
163+
Then, while performing an update, the "Updating RoT" step will be simulated as skipped.
164+
165+
![Screenshot showing that the "Updating RoT" step has a "skipped" status with a message saying "Simulated skipped result"](https://user-images.githubusercontent.com/180618/254689686-99259bc0-4e68-421d-98ca-362774eef155.png).
166+
139167
## Testing upload functionality
140168

141169
Test upload functionality without setting up wicket as an SSH captive shell (see below for instructions). (This is the most common use case.)

wicket/src/runner.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use tui::Terminal;
2828
use wicketd_client::types::AbortUpdateOptions;
2929
use wicketd_client::types::ClearUpdateStateOptions;
3030
use wicketd_client::types::StartUpdateOptions;
31+
use wicketd_client::types::UpdateSimulatedResult;
3132
use wicketd_client::types::UpdateTestError;
3233

3334
use crate::events::EventReportMap;
@@ -191,9 +192,18 @@ impl RunnerCore {
191192
)
192193
});
193194

195+
let test_simulate_rot_result = get_update_simulated_result(
196+
"WICKET_UPDATE_TEST_SIMULATE_ROT_RESULT",
197+
)?;
198+
let test_simulate_sp_result = get_update_simulated_result(
199+
"WICKET_UPDATE_TEST_SIMULATE_SP_RESULT",
200+
)?;
201+
194202
let options = StartUpdateOptions {
195203
test_error,
196204
test_step_seconds,
205+
test_simulate_rot_result,
206+
test_simulate_sp_result,
197207
skip_rot_version_check: self
198208
.state
199209
.force_update_state
@@ -307,6 +317,26 @@ fn get_update_test_error(
307317
Ok(test_error)
308318
}
309319

320+
fn get_update_simulated_result(
321+
env_var: &str,
322+
) -> Result<Option<UpdateSimulatedResult>, anyhow::Error> {
323+
let result = match std::env::var(env_var) {
324+
Ok(v) if v == "success" => Some(UpdateSimulatedResult::Success),
325+
Ok(v) if v == "warning" => Some(UpdateSimulatedResult::Warning),
326+
Ok(v) if v == "skipped" => Some(UpdateSimulatedResult::Skipped),
327+
Ok(v) if v == "failure" => Some(UpdateSimulatedResult::Failure),
328+
Ok(value) => {
329+
bail!("unrecognized value for {env_var}: {value}");
330+
}
331+
Err(VarError::NotPresent) => None,
332+
Err(VarError::NotUnicode(value)) => {
333+
bail!("invalid Unicode for {env_var}: {}", value.to_string_lossy());
334+
}
335+
};
336+
337+
Ok(result)
338+
}
339+
310340
/// The `Runner` owns the main UI thread, and starts a tokio runtime
311341
/// for interaction with downstream services.
312342
pub struct Runner {

0 commit comments

Comments
 (0)