Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/core/mxc_config_contract/src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,15 @@ pub enum Version {
}
}

/// The development `0.8.0-alpha` configuration contract.
mod experimental;
mod network;
/// The development `0.8.0-alpha` one-shot configuration contract.
mod one_shot;
mod primitives;
mod request;
mod stable;
/// The development `0.8.0-alpha` state-aware configuration contract.
mod state_aware;

pub use experimental::{
OneShotExperimental, OneShotWindowsSandbox, OneShotWslc, PortMapping, Telemetry, TestFeature,
Expand All @@ -117,8 +120,27 @@ pub use experimental::{
pub use network::{DefaultNetworkPolicy, Network, NetworkEnforcementMode, NetworkProxy};
pub use one_shot::{Containment as OneShotContainment, Request as OneShotRequest};
pub use primitives::{NonEmptyString, OptionalField, True};
pub use request::{parse_request, Request, RequestParseError};
pub use stable::{
CaptureDenials, CaptureDenialsMode, Fallback, Filesystem, LaunchMethod, Lifecycle, Lxc,
Process, ProcessContainer, ProcessContainerUi, ProcessContainerUiIsolation, Seatbelt, Ui,
UiClipboard,
};
pub use state_aware::{probe_containment, Containment, ContainmentProbeError};
pub use state_aware::{probe_phase, Phase, PhaseProbeError};
pub use state_aware::{DeprovisionExperimental, DeprovisionPhase, DeprovisionRequest};
pub use state_aware::{ExecExperimental, ExecPhase, ExecRequest};
pub use state_aware::{
IsolationSessionContainment, IsolationSessionNetwork, IsolationSessionNetworkDefaultPolicy,
IsolationSessionProvision, IsolationSessionProvisionExperimental,
IsolationSessionProvisionRequest, StateAwareIsolationSession,
};
pub use state_aware::{ProvisionPhase, ProvisionRequest};
pub use state_aware::{StartExperimental, StartPhase, StartRequest};
pub use state_aware::{
StateAwareWslc, WslcContainment, WslcProvision, WslcProvisionExperimental, WslcProvisionRequest,
};
pub use state_aware::{StopExperimental, StopPhase, StopRequest};
pub use state_aware::{
WindowsSandboxContainment, WindowsSandboxExperimental, WindowsSandboxProvisionRequest,
};
95 changes: 95 additions & 0 deletions src/core/mxc_config_contract/src/dev/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use super::one_shot::Request as OneShotRequest;
use super::state_aware::{probe_containment, Containment, ContainmentProbeError};
use super::state_aware::{probe_phase, Phase, PhaseProbeError};
use super::state_aware::{
DeprovisionRequest, ExecRequest, ProvisionRequest, StartRequest, StopRequest,
};

/// A validated request for the mutable `0.8.0-alpha` development contract.
#[derive(Debug)]
pub enum Request {
/// A one-shot execution request with no lifecycle phase.
OneShot(Box<OneShotRequest>),
/// A state-aware provision request selected by its containment backend.
Provision(ProvisionRequest),
/// A state-aware start request.
Start(StartRequest),
/// A state-aware process-execution request.
Exec(ExecRequest),
/// A state-aware stop request.
Stop(StopRequest),
/// A state-aware deprovision request.
Deprovision(DeprovisionRequest),
}

/// An error encountered while selecting or deserializing a development request.
#[derive(Debug, thiserror::Error)]
pub enum RequestParseError {
/// The lifecycle phase declaration is malformed or unsupported.
#[error("Invalid phase declaration")]
Phase(#[from] PhaseProbeError),

/// A provision request's containment declaration is malformed or unsupported.
#[error("Invalid provision containment declaration")]
Containment(#[from] ContainmentProbeError),

/// The selected request contract rejected the complete document.
#[error("Invalid {contract} request")]
InvalidRequest {
/// Human-readable name of the selected contract.
contract: &'static str,
/// Structured deserialization error from the selected contract.
#[source]
source: serde_json::Error,
},
}

fn deserialize<T>(json: &str, contract: &'static str) -> Result<T, RequestParseError>
where
T: serde::de::DeserializeOwned,
{
serde_json::from_str(json)
.map_err(|source| RequestParseError::InvalidRequest { contract, source })
}

fn parse_provision(json: &str) -> Result<ProvisionRequest, RequestParseError> {
match probe_containment(json)? {
Containment::WindowsSandbox => {
deserialize(json, "Windows Sandbox provision").map(ProvisionRequest::WindowsSandbox)
}
Containment::IsolationSession => {
deserialize(json, "IsolationSession provision").map(ProvisionRequest::IsolationSession)
}
Containment::Wslc => deserialize(json, "WSLC provision").map(ProvisionRequest::Wslc),
}
}

/// Selects and deserializes one exact development request from raw JSON source.
///
/// An absent `phase` selects the one-shot contract. A present phase selects its
/// corresponding state-aware contract, with provision requests additionally
/// selected by their required `containment` declaration. The selected concrete
/// request still requires the exact `0.8.0-alpha` version marker.
///
/// # Errors
///
/// Returns [`RequestParseError::Phase`] when the phase declaration is malformed
/// or unsupported, [`RequestParseError::Containment`] when a provision
/// containment declaration is malformed or unsupported, and
/// [`RequestParseError::InvalidRequest`] when the selected concrete contract
/// rejects the document.
pub fn parse_request(json: &str) -> Result<Request, RequestParseError> {
match probe_phase(json)? {
None => deserialize(json, "one-shot")
.map(Box::new)
.map(Request::OneShot),
Some(Phase::Provision) => parse_provision(json).map(Request::Provision),
Some(Phase::Start) => deserialize(json, "start").map(Request::Start),
Some(Phase::Exec) => deserialize(json, "exec").map(Request::Exec),
Some(Phase::Stop) => deserialize(json, "stop").map(Request::Stop),
Some(Phase::Deprovision) => deserialize(json, "deprovision").map(Request::Deprovision),
}
}
46 changes: 46 additions & 0 deletions src/core/mxc_config_contract/src/dev/state_aware/deprovision.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::dev::experimental::Telemetry;
use crate::dev::{OptionalField, Version};
use serde::Deserialize;

string_marker! {
/// The `deprovision` phase of the state-aware configuration contract.
pub struct DeprovisionPhase => "deprovision";
}

/// Experimental settings accepted by the `deprovision` phase.
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DeprovisionExperimental {
/// Optional telemetry override.
#[serde(default)]
pub telemetry: OptionalField<Telemetry>,
}

/// A complete state-aware `deprovision` request.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DeprovisionRequest {
/// Optional JSON Schema reference for editor validation.
#[serde(rename = "$schema", default)]
pub schema: OptionalField<String>,
/// Optional human-readable annotation ignored by the runtime.
#[serde(rename = "_comment", default)]
pub comment: OptionalField<serde_json::Value>,
/// Exact development contract version.
pub version: Version,
/// Exact `deprovision` phase marker.
pub phase: DeprovisionPhase,
/// Identifier of the sandbox to deprovision.
pub sandbox_id: String,

/// Optional correlation vector relayed from provision.
#[serde(default)]
pub correlation_vector: OptionalField<String>,

/// Optional closed post-provision experimental settings.
#[serde(default)]
pub experimental: OptionalField<DeprovisionExperimental>,
}
54 changes: 54 additions & 0 deletions src/core/mxc_config_contract/src/dev/state_aware/exec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::dev::experimental::Telemetry;
use crate::dev::{Network, Process};
use crate::dev::{OptionalField, Version};
use serde::Deserialize;

string_marker! {
/// The `exec` phase of the state-aware configuration contract.
pub struct ExecPhase => "exec";
}

/// Experimental settings accepted by the `exec` phase.
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ExecExperimental {
/// Optional telemetry override.
#[serde(default)]
pub telemetry: OptionalField<Telemetry>,
}

/// A complete state-aware `exec` request.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ExecRequest {
/// Optional JSON Schema reference for editor validation.
#[serde(rename = "$schema", default)]
pub schema: OptionalField<String>,
/// Optional human-readable annotation ignored by the runtime.
#[serde(rename = "_comment", default)]
pub comment: OptionalField<serde_json::Value>,
/// Exact development contract version.
pub version: Version,
/// Exact `exec` phase marker.
pub phase: ExecPhase,
/// Identifier of the sandbox to execute in.
pub sandbox_id: String,

/// Optional correlation vector relayed from provision.
#[serde(default)]
pub correlation_vector: OptionalField<String>,

/// Process to execute in the sandbox.
pub process: Process,

/// Optional per-execution network settings.
#[serde(default)]
pub network: OptionalField<Network>,

/// Optional closed exec experimental settings.
#[serde(default)]
pub experimental: OptionalField<ExecExperimental>,
}
57 changes: 57 additions & 0 deletions src/core/mxc_config_contract/src/dev/state_aware/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Wire types for the mutable `0.8.0-alpha` state-aware configuration
//! contract.

macro_rules! string_marker {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident => $wire_value:literal;
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
$vis struct $name;

impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <std::borrow::Cow<'de, str> as serde::Deserialize>::deserialize(deserializer)?;

if value == $wire_value {
Ok(Self)
} else {
Err(serde::de::Error::unknown_variant(&value, &[$wire_value]))
}
}
}
};
}

mod deprovision;
mod exec;
mod phase;
mod provision;
mod start;
mod stop;

pub use deprovision::{DeprovisionExperimental, DeprovisionPhase, DeprovisionRequest};
pub use exec::{ExecExperimental, ExecPhase, ExecRequest};
pub use phase::{probe_phase, Phase, PhaseProbeError};
pub use provision::{probe_containment, Containment, ContainmentProbeError};
pub use provision::{
IsolationSessionContainment, IsolationSessionNetwork, IsolationSessionNetworkDefaultPolicy,
IsolationSessionProvision, IsolationSessionProvisionExperimental,
IsolationSessionProvisionRequest, StateAwareIsolationSession,
};
pub use provision::{ProvisionPhase, ProvisionRequest};
pub use provision::{
StateAwareWslc, WslcContainment, WslcProvision, WslcProvisionExperimental, WslcProvisionRequest,
};
pub use provision::{
WindowsSandboxContainment, WindowsSandboxExperimental, WindowsSandboxProvisionRequest,
};
pub use start::{StartExperimental, StartPhase, StartRequest};
pub use stop::{StopExperimental, StopPhase, StopRequest};
Loading
Loading