Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

states: Add state change callback for error state and minor fixes #352

Merged
merged 4 commits into from
Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion updatehub/src/states/direct_download.rs
Expand Up @@ -17,7 +17,7 @@ pub(super) struct DirectDownload {

impl CommunicationState for DirectDownload {}

#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for DirectDownload {
fn name(&self) -> &'static str {
"direct_download"
Expand Down
7 changes: 4 additions & 3 deletions updatehub/src/states/download.rs
Expand Up @@ -4,7 +4,7 @@

use super::{
machine::{self, CommunicationState, Context},
Install, ProgressReporter, Result, State, StateChangeImpl, TransitionError,
CallbackReporter, Install, ProgressReporter, Result, State, StateChangeImpl, TransitionError,
};
use crate::{
firmware::installation_set,
Expand Down Expand Up @@ -77,7 +77,8 @@ impl Download {
}
}

#[async_trait::async_trait(?Send)]
impl CallbackReporter for Download {}

impl ProgressReporter for Download {
fn package_uid(&self) -> String {
self.update_package.package_uid()
Expand All @@ -94,7 +95,7 @@ impl ProgressReporter for Download {

impl CommunicationState for Download {}

#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Download {
fn name(&self) -> &'static str {
"download"
Expand Down
2 changes: 1 addition & 1 deletion updatehub/src/states/entry_point.rs
Expand Up @@ -16,7 +16,7 @@ pub(super) struct EntryPoint {}
///
/// If polling is disabled it stays in `State<EntryPoint>`, otherwise, it moves
/// to `State<Poll>` state.
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for EntryPoint {
fn name(&self) -> &'static str {
"entry_point"
Expand Down
6 changes: 4 additions & 2 deletions updatehub/src/states/error.rs
Expand Up @@ -4,7 +4,7 @@

use super::{
machine::{self, Context},
EntryPoint, Result, State, StateChangeImpl, TransitionError,
CallbackReporter, EntryPoint, Result, State, StateChangeImpl, TransitionError,
};

use crate::firmware;
Expand All @@ -15,7 +15,9 @@ pub(super) struct Error {
error: TransitionError,
}

#[async_trait::async_trait(?Send)]
impl CallbackReporter for Error {}

#[async_trait::async_trait]
impl StateChangeImpl for Error {
fn name(&self) -> &'static str {
"error"
Expand Down
6 changes: 4 additions & 2 deletions updatehub/src/states/install.rs
Expand Up @@ -4,7 +4,7 @@

use super::{
machine::{self, Context},
ProgressReporter, Reboot, Result, State, StateChangeImpl,
CallbackReporter, ProgressReporter, Reboot, Result, State, StateChangeImpl,
};
use crate::{
firmware::installation_set,
Expand All @@ -18,6 +18,8 @@ pub(super) struct Install {
pub(super) update_package: UpdatePackage,
}

impl CallbackReporter for Install {}

impl ProgressReporter for Install {
fn package_uid(&self) -> String {
self.update_package.package_uid()
Expand All @@ -32,7 +34,7 @@ impl ProgressReporter for Install {
}
}

#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Install {
fn name(&self) -> &'static str {
"install"
Expand Down
32 changes: 27 additions & 5 deletions updatehub/src/states/mod.rs
Expand Up @@ -59,7 +59,7 @@ pub enum TransitionError {
Process(easy_process::Error),
}

#[async_trait(?Send)]
#[async_trait]
trait StateChangeImpl {
async fn handle(
self,
Expand All @@ -82,8 +82,30 @@ trait StateChangeImpl {
}
}

#[async_trait(?Send)]
trait ProgressReporter: Sized + StateChangeImpl {
#[async_trait]
trait CallbackReporter: Sized + StateChangeImpl {
async fn handle_with_callback(
self,
context: &mut machine::Context,
) -> Result<(State, machine::StepTransition)> {
let transition =
firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

match transition {
Transition::Continue => Ok(self.handle(context).await?),
Transition::Cancel => {
info!(
"cancelling transition to '{}' due to state change callback request",
self.name()
);
Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
}
}
}
}

#[async_trait]
trait ProgressReporter: CallbackReporter {
fn package_uid(&self) -> String;
fn report_enter_state_name(&self) -> &'static str;
fn report_leave_state_name(&self) -> &'static str;
Expand Down Expand Up @@ -195,7 +217,7 @@ fn handle_startup_callbacks(
Ok(())
}

#[async_trait(?Send)]
#[async_trait]
impl StateChangeImpl for State {
async fn handle(self, st: &mut machine::Context) -> Result<(State, machine::StepTransition)> {
trace!("starting to handle: {}", self.name());
Expand Down Expand Up @@ -225,14 +247,14 @@ impl State {
context: &mut machine::Context,
) -> Result<(Self, machine::StepTransition)> {
match self {
State::Error(s) => s.handle(context).await,
State::Park(s) => s.handle(context).await,
State::EntryPoint(s) => s.handle(context).await,
State::Poll(s) => s.handle(context).await,
State::Probe(s) => s.handle(context).await,
State::Validation(s) => s.handle(context).await,
State::DirectDownload(s) => s.handle(context).await,
State::PrepareLocalInstall(s) => s.handle(context).await,
State::Error(s) => s.handle_with_callback(context).await,
State::Download(s) => s.handle_with_callback_and_report_progress(context).await,
State::Install(s) => s.handle_with_callback_and_report_progress(context).await,
State::Reboot(s) => s.handle_with_callback_and_report_progress(context).await,
Expand Down
2 changes: 1 addition & 1 deletion updatehub/src/states/park.rs
Expand Up @@ -13,7 +13,7 @@ pub(super) struct Park {}

/// Implements the state change for `State<Park>`. It stays in
/// `State<Park>` state.
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Park {
fn name(&self) -> &'static str {
"park"
Expand Down
2 changes: 1 addition & 1 deletion updatehub/src/states/poll.rs
Expand Up @@ -15,7 +15,7 @@ pub(super) struct Poll {}
/// Implements the state change for `State<Poll>`.
///
/// This state is used to control when to go to the `State<Probe>`.
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Poll {
fn name(&self) -> &'static str {
"poll"
Expand Down
2 changes: 1 addition & 1 deletion updatehub/src/states/prepare_local_install.rs
Expand Up @@ -22,7 +22,7 @@ pub(super) struct PrepareLocalInstall {
pub(super) update_file: PathBuf,
}

#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for PrepareLocalInstall {
fn name(&self) -> &'static str {
"prepare_local_install"
Expand Down
2 changes: 1 addition & 1 deletion updatehub/src/states/probe.rs
Expand Up @@ -15,7 +15,7 @@ use std::time::Duration;
pub(super) struct Probe;

/// Implements the state change for State<Probe>.
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Probe {
fn name(&self) -> &'static str {
"probe"
Expand Down
6 changes: 4 additions & 2 deletions updatehub/src/states/reboot.rs
Expand Up @@ -4,7 +4,7 @@

use super::{
machine::{self, Context},
EntryPoint, ProgressReporter, Result, State, StateChangeImpl,
CallbackReporter, EntryPoint, ProgressReporter, Result, State, StateChangeImpl,
};
use crate::update_package::UpdatePackage;
use slog_scope::{info, warn};
Expand All @@ -14,6 +14,8 @@ pub(super) struct Reboot {
pub(super) update_package: UpdatePackage,
}

impl CallbackReporter for Reboot {}

impl ProgressReporter for Reboot {
fn package_uid(&self) -> String {
self.update_package.package_uid()
Expand All @@ -28,7 +30,7 @@ impl ProgressReporter for Reboot {
}
}

#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Reboot {
fn name(&self) -> &'static str {
"reboot"
Expand Down
15 changes: 12 additions & 3 deletions updatehub/src/states/validation.rs
Expand Up @@ -16,7 +16,7 @@ pub(super) struct Validation {
}

/// Implements the state change for State<Validation>.
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl StateChangeImpl for Validation {
fn name(&self) -> &'static str {
"validation"
Expand Down Expand Up @@ -46,10 +46,19 @@ impl StateChangeImpl for Validation {
let inactive_installation_set = context.runtime_settings.get_inactive_installation_set()?;
self.package.compatible_with(&context.firmware)?;
self.package.validate_install_modes(&context.settings, inactive_installation_set)?;
self.package
if let Err(e) = self
.package
.objects(inactive_installation_set)
.iter()
.try_for_each(object::Installer::check_requirements)?;
.try_for_each(object::Installer::check_requirements)
{
error!(
"update package: {} ({}) has failed to meet the install requirements",
self.package.version(),
self.package.package_uid()
);
return Err(e.into());
}

if context
.runtime_settings
Expand Down
3 changes: 3 additions & 0 deletions updatehub/tests/failed_integration_test.rs
Expand Up @@ -200,6 +200,7 @@ fn failing_fail_check_requirements() {
<timestamp> INFO probing server as we are in time
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> ERRO error state reached: fail to check the requirements
<timestamp> INFO returning to machine's entry point
"###);
Expand All @@ -218,6 +219,7 @@ fn failing_fail_check_requirements() {
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> TRCE starting to handle: validation
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> TRCE starting to handle: error
<timestamp> ERRO error state reached: fail to check the requirements
<timestamp> INFO returning to machine's entry point
Expand All @@ -231,6 +233,7 @@ fn failing_fail_check_requirements() {
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> TRCE starting to handle: validation
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> TRCE starting to handle: error
<timestamp> ERRO error state reached: fail to check the requirements
<timestamp> INFO returning to machine's entry point
Expand Down
67 changes: 67 additions & 0 deletions updatehub/tests/successful_integration_test.rs
Expand Up @@ -387,3 +387,70 @@ echo "cancel"
<timestamp> DEBG receiving log request
"###);
}

#[test]
fn correct_config_error_state_callback() {
let state_change_script = r#"#! /bin/bash
echo "cancel"
"#;

let (mut session, setup) = Settings::default()
.timeout(300)
.polling()
.state_change_callback(state_change_script)
.init_server();
let _mocks = create_mock_server(FakeServer::CheckRequirementsTest(
setup.firmware.data.product_uid.clone(),
));

let (output_server_trce, output_server_info) =
get_output_server(&mut session, StopMessage::Polling(Polling::Enable));
let output_log = run_client_log(&setup.settings.data.network.listen_socket);

insta::assert_snapshot!(output_server_info, @r###"
<timestamp> INFO starting UpdateHub Agent <version>
<timestamp> INFO probing server as we are in time
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> INFO cancelling transition to 'error' due to state change callback request
"###);

insta::assert_snapshot!(output_server_trce, @r###"
<timestamp> INFO starting UpdateHub Agent <version>
<timestamp> DEBG loading system settings from "<file>"
<timestamp> DEBG runtime settings file "<file>" does not exists, using default settings
<timestamp> TRCE starting to handle: entry_point
<timestamp> DEBG polling is enabled
<timestamp> TRCE starting to handle: poll
<timestamp> INFO probing server as we are in time
<timestamp> TRCE starting to handle: probe
<timestamp> DEBG updating last polling time
<timestamp> DEBG saved runtime settings to "<file>"
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> TRCE starting to handle: validation
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> TRCE starting to handle: error
<timestamp> INFO cancelling transition to 'error' due to state change callback request
<timestamp> TRCE starting to handle: entry_point
<timestamp> DEBG polling is enabled
<timestamp> TRCE starting to handle: poll
<timestamp> DEBG delaying <time> till next probe
"###);

insta::assert_snapshot!(output_log, @r###"
<timestamp> INFO update received: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3)
<timestamp> TRCE starting to handle: validation
<timestamp> INFO no signature key available on device, ignoring signature validation
<timestamp> ERRO update package: 1.2 (fb21b217cb83e8af368c773eb13bad0a94e1b0088c6bf561072decf3c1ae9df3) has failed to meet the install requirements
<timestamp> TRCE starting to handle: error
<timestamp> INFO cancelling transition to 'error' due to state change callback request
<timestamp> TRCE starting to handle: entry_point
<timestamp> DEBG polling is enabled
<timestamp> TRCE starting to handle: poll
<timestamp> DEBG delaying <time> till next probe
<timestamp> TRCE delaying transition for: <time>
<timestamp> DEBG receiving log request
"###);
}