omaha_client 0.3.8

Platform- and product-agnostic implementation of the client end of the Omaha Protocol
Documentation
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.

use crate::{
    common::{App, CheckOptions, CheckTiming, ProtocolState, UpdateCheckSchedule},
    installer::Plan,
    request_builder::RequestParams,
    time::{ComplexTime, TimeSource},
};
use futures::future::BoxFuture;

#[cfg(test)]
mod mock;
#[cfg(test)]
pub use mock::MockPolicyEngine;
mod stub;
pub use stub::StubPolicy;
pub use stub::StubPolicyEngine;
use typed_builder::TypedBuilder;

/// Data about the local system that's needed to fulfill Policy questions
#[derive(Clone, Debug, TypedBuilder)]
pub struct PolicyData {
    /// The current time at the start of the update
    #[builder(setter(into))]
    pub current_time: ComplexTime,
}

/// Reasons why a check can/cannot be performed at this time
#[derive(Clone, Debug, PartialEq)]
pub enum CheckDecision {
    /// positive responses
    Ok(RequestParams),
    /// but with caveats:
    OkUpdateDeferred(RequestParams),

    /// negative responses
    TooSoon,
    ThrottledByPolicy,
    DeniedByPolicy,
}

#[cfg(test)]
impl Default for CheckDecision {
    fn default() -> Self {
        CheckDecision::Ok(RequestParams::default())
    }
}

/// Reasons why an update can/cannot be performed at this time
#[derive(Clone, Debug, PartialEq)]
pub enum UpdateDecision {
    /// Update can be performed.
    Ok,
    /// Update is deferred by Policy.
    DeferredByPolicy,
    /// Update is rejected by Policy.
    DeniedByPolicy,
}

#[cfg(test)]
// Disable clippy lint for this impl as we want to derive it manually so that it is only available
// for tests.
#[allow(clippy::derivable_impls)]
impl Default for UpdateDecision {
    fn default() -> Self {
        UpdateDecision::Ok
    }
}

/// The policy implementation itself
pub trait Policy {
    type ComputeNextUpdateTimePolicyData;
    type UpdateCheckAllowedPolicyData;
    type UpdateCanStartPolicyData;
    type RebootPolicyData;
    type InstallPlan: Plan;

    /// When should the next update happen?
    fn compute_next_update_time(
        policy_data: &Self::ComputeNextUpdateTimePolicyData,
        apps: &[App],
        scheduling: &UpdateCheckSchedule,
        protocol_state: &ProtocolState,
    ) -> CheckTiming;

    /// Given the current State, and the current PolicyData, is an update check
    /// allowed at this time.  A CheckDecision is used to return the reasoning, as in
    /// some cases, instead of an update check, the SM will instead notify Omaha that
    /// it would perform an update, but instead just tell the device whether or not
    /// an update is available.
    fn update_check_allowed(
        policy_data: &Self::UpdateCheckAllowedPolicyData,
        apps: &[App],
        scheduling: &UpdateCheckSchedule,
        protocol_state: &ProtocolState,
        check_options: &CheckOptions,
    ) -> CheckDecision;

    /// Given the current State, the current PolicyData, can the proposed InstallPlan
    /// be executed at this time.
    fn update_can_start(
        policy_data: &Self::UpdateCanStartPolicyData,
        proposed_install_plan: &Self::InstallPlan,
    ) -> UpdateDecision;

    /// Given the current PolicyData, is reboot allowed right now.
    fn reboot_allowed(policy_data: &Self::RebootPolicyData, check_options: &CheckOptions) -> bool;

    /// Given the InstallPlan, is reboot needed after update has been installed.
    fn reboot_needed(install_plan: &Self::InstallPlan) -> bool;
}

pub trait PolicyEngine {
    type TimeSource: TimeSource + Clone;
    type InstallResult;
    type InstallPlan: Plan;

    /// Provides the time source used by the PolicyEngine to the state machine.
    fn time_source(&self) -> &Self::TimeSource;

    /// When should the next update happen?
    fn compute_next_update_time<'a>(
        &'a mut self,
        apps: &'a [App],
        scheduling: &'a UpdateCheckSchedule,
        protocol_state: &'a ProtocolState,
    ) -> BoxFuture<'a, CheckTiming>;

    /// Given the context provided by State, does the Policy allow an update check to
    /// happen at this time?  This should be consistent with the compute_next_update_time
    /// so that during background updates, the result of compute_next_update_time will
    /// result in a CheckDecision::Ok() value from this function.
    fn update_check_allowed<'a>(
        &'a mut self,
        apps: &'a [App],
        scheduling: &'a UpdateCheckSchedule,
        protocol_state: &'a ProtocolState,
        check_options: &'a CheckOptions,
    ) -> BoxFuture<'a, CheckDecision>;

    /// Given the current State, the current PolicyData, can the proposed InstallPlan
    /// be executed at this time.
    fn update_can_start<'a>(
        &'a mut self,
        proposed_install_plan: &'a Self::InstallPlan,
    ) -> BoxFuture<'a, UpdateDecision>;

    /// Is reboot allowed right now.
    fn reboot_allowed<'a>(
        &'a mut self,
        check_options: &'a CheckOptions,
        install_result: &'a Self::InstallResult,
    ) -> BoxFuture<'a, bool>;

    /// Given the InstallPlan, is reboot needed after update has been installed.
    fn reboot_needed<'a>(&'a mut self, install_plan: &'a Self::InstallPlan) -> BoxFuture<'a, bool>;
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::time::MockTimeSource;

    #[test]
    pub fn test_policy_data_builder_with_system_time() {
        let current_time = MockTimeSource::new_from_now().now();
        let policy_data = PolicyData::builder().current_time(current_time).build();
        assert_eq!(policy_data.current_time, current_time);
    }

    #[test]
    pub fn test_policy_data_builder_with_clock() {
        let source = MockTimeSource::new_from_now();
        let current_time = source.now();
        let policy_data = PolicyData::builder().current_time(source.now()).build();
        assert_eq!(policy_data.current_time, current_time);
    }
}