-
Notifications
You must be signed in to change notification settings - Fork 63
[sled-agent] Create an "Executor", which intercepts requests through std::process::Command #3442
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
Open
smklein
wants to merge
94
commits into
main
Choose a base branch
from
executor-fake
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
94 commits
Select commit
Hold shift + click to select a range
d2e9582
Refactor test setup as builder, pull DNS server out of simulated sled…
smklein c41ff8a
[nexus] Resilience to arbitrary boot order
smklein d6a3919
Merge branch 'main' into resilient-nexus-builder
smklein 4ba64cf
Handler Task Mode
smklein b76f9e9
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 9e9a4eb
Ipv6 CockroachDB addresses
smklein c49eed2
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 9d91fb2
[sled agent] Clarify LazyNexusClient constructor arguments, cache add…
smklein 492666a
Merge branch 'main' into resilient-nexus-builder
smklein 65e0473
Update docs around how-to-run-simulated
smklein 4046281
Updated comment
smklein 93bcca6
Add logging
smklein 6a6187b
Add note about test replacement
smklein e340a04
Update comment about DNS w.r.t. pantry server
smklein d0baed2
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 11c37b7
Merge branch 'main' into resilient-nexus-builder
smklein f80788a
Review feedback
smklein 30807c6
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 0a476d9
Merge branch 'resilient-nexus-boot-order' into lazy-nexus
smklein 8c50d38
server to dns_server
smklein 3323815
Merge branch 'resilient-nexus-boot-order' into lazy-nexus
smklein f06942e
Attempting to pull DNS resolver into progenitor client, tbd
smklein 4ac3140
Merge branch 'main' into resilient-nexus-builder
smklein e745c76
Create sled agent config parameter to specify internal DNS
smklein e60c666
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 6f5926c
Merge branch 'resilient-nexus-boot-order' into lazy-nexus
smklein 4ea32bc
Test that we can use the internal DNS resolver with progenitor
smklein 05109dc
Deprecate 'LazyNexusClient', relying on internal DNS resolver instead
smklein 55b0178
clippy, comments
smklein fb74e5c
Comments
smklein 259f5ec
Deal with mocks
smklein d8efb4a
Avoid mocks in the update test, add a nexus fake
smklein 97223ae
Continue to reduce usage of mocks to represent NexusClient
smklein d5fcafc
Add Executor trait, use it
smklein 96edc93
Remove unused mocks
smklein b9ef267
Add dladm tests
smklein 2a49520
docs
smklein 2ded293
Merge branch 'main' into resilient-nexus-builder
smklein d9ca175
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein 2526ca9
Merge branch 'resilient-nexus-boot-order' into lazy-nexus
smklein 701f2ac
Merge branch 'lazy-nexus' into less-mocks
smklein b76eaca
Merge branch 'main' into resilient-nexus-builder
smklein d4af9a0
Merge branch 'resilient-nexus-builder' into resilient-nexus-boot-order
smklein bcbe2e8
Merge branch 'resilient-nexus-boot-order' into lazy-nexus
smklein a62bcc1
Merge branch 'lazy-nexus' into less-mocks
smklein c5ac608
Merge branch 'less-mocks' into executor-fake
smklein 0b85e12
Merge branch 'main' into lazy-nexus, Review Feedback
smklein 711a3ff
Merge branch 'lazy-nexus' into less-mocks
smklein fa33af7
Merge branch 'less-mocks' into executor-fake, make illumos happy
smklein 909d9ba
Okay now happy on non-illumos too
smklein b05c6aa
clippy
smklein 03abbd1
Merge branch 'main' into lazy-nexus
smklein 4d411a6
Merge branch 'lazy-nexus' into less-mocks
smklein 0f3d24b
Merge branch 'less-mocks' into executor-fake
smklein dab59ff
Merge branch 'main' into executor-fake
smklein 9767d67
Add support for spawning fake processes too
smklein 3df0260
Merge branch 'main' into executor-fake
smklein 091ac11
Update cargo.lock, appease clippy
smklein baf35ed
Merge branch 'main' into executor-fake
smklein 49a9b35
More attempts to make compilation on illumos happy
smklein 837ec37
Remove all usage of mocks
smklein dc3a6aa
Clippy for tests
smklein 629ed64
Deduplicate
smklein 526e42a
Merge branch 'main' into executor-fake
smklein 3420a9d
less echo
smklein 673b0ed
Refactor emulated host
smklein 544aaef
Tests passing on illumos
smklein a57d4db
Flip disposition of registration
smklein eaa1ce1
Merge branch 'main' into executor-fake
smklein 27378dd
cleanup
smklein 172a8e3
Merge branch 'main' into executor-fake
smklein e6ee96f
Less boxy errors
smklein 2954fd2
One less clone
smklein 5bfc6ea
Better slog citizen
smklein 6d85d90
fn wait consumes box
smklein eece3b1
take stdin
smklein be077b3
CommandSequence
smklein 2d281ee
Into string
smklein 9acb7b8
output_to_exec error is now ExecutionError::from_output
smklein 6c19b40
Use shlex for strong quoting
smklein cd332e6
less allocations while logging
smklein be2454b
modules, satiate clippy
smklein 846840c
Builder for FakeExecutor
smklein f22548c
Fix helios tests
smklein ad7e3a4
Fix docs
smklein 5272ca9
Split into helios-fusion, helios-tokamak, helios-protostar crates
smklein fa07b40
SharedByteQueue
smklein 974e6b6
Merge branch 'main' into executor-fake
smklein 44b9f85
Merge branch 'main' into executor-fake
smklein b016f0d
patch docs
smklein f17f24e
Merge branch 'main' into executor-fake
smklein db56d0d
Merge branch 'main' into executor-fake
smklein 6790409
hakari
smklein 108f4d6
more hakari
smklein File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| :showtitle: | ||
| :toc: left | ||
| :icons: font | ||
|
|
||
| = helios | ||
|
|
||
| This directory describes various interfaces for acting upon a Helios system. | ||
|
|
||
| * `fusion` describes an interface for interacting with the underlying OS. | ||
| * `tokamak` provides a fake implementation of `fusion` - not enough | ||
| to be virtualized, but enough to test code depending on `fusion` under test. | ||
| * `protostar` provides a real implementation of `fusion`, which should actually | ||
| make calls to the underlying OS. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| [package] | ||
| name = "helios-fusion" | ||
| description = "Interface to access an underlying Helios system" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| license = "MPL-2.0" | ||
|
|
||
| [dependencies] | ||
| async-trait.workspace = true | ||
| itertools.workspace = true | ||
| shlex.workspace = true | ||
| slog.workspace = true | ||
| thiserror.workspace = true | ||
| tokio.workspace = true | ||
| omicron-workspace-hack = { version = "0.1", path = "../../workspace-hack" } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| use slog::error; | ||
|
|
||
| #[derive(Debug)] | ||
| pub struct FailureInfo { | ||
| pub command: String, | ||
| pub status: std::process::ExitStatus, | ||
| pub stdout: String, | ||
| pub stderr: String, | ||
| } | ||
|
|
||
| impl std::fmt::Display for FailureInfo { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
| write!( | ||
| f, | ||
| "Command [{}] executed and failed with status: {}", | ||
| self.command, self.status | ||
| )?; | ||
| write!(f, " stdout: {}", self.stdout)?; | ||
| write!(f, " stderr: {}", self.stderr) | ||
| } | ||
| } | ||
|
|
||
| #[derive(thiserror::Error, Debug)] | ||
| pub enum ExecutionError { | ||
| #[error("Failed to start execution of [{command}]: {err}")] | ||
| ExecutionStart { command: String, err: std::io::Error }, | ||
|
|
||
| #[error("{0}")] | ||
| CommandFailure(Box<FailureInfo>), | ||
|
|
||
| #[error("Failed to manipulate process contract: {err}")] | ||
| ContractFailure { err: std::io::Error }, | ||
|
|
||
| #[error("Zone not running")] | ||
| NotRunning, | ||
| } | ||
|
|
||
| /// Convenience trait for turning [std::process::Command] into a String. | ||
| pub trait AsCommandStr { | ||
| fn into_str(&self) -> String; | ||
| } | ||
|
|
||
| impl AsCommandStr for String { | ||
| fn into_str(&self) -> String { | ||
| self.into() | ||
| } | ||
| } | ||
|
|
||
| impl AsCommandStr for &std::process::Command { | ||
| fn into_str(&self) -> String { | ||
| shlex::join( | ||
| std::iter::once(self.get_program()) | ||
| .chain(self.get_args()) | ||
| .map(|s| s.to_str().expect("Invalid UTF-8")), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| impl ExecutionError { | ||
| pub fn from_output<S: AsCommandStr>( | ||
| command: S, | ||
| output: &std::process::Output, | ||
| ) -> Self { | ||
| Self::CommandFailure(Box::new(FailureInfo { | ||
| command: command.into_str(), | ||
| status: output.status, | ||
| stdout: String::from_utf8_lossy(&output.stdout).to_string(), | ||
| stderr: String::from_utf8_lossy(&output.stderr).to_string(), | ||
| })) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| //! Interfaces used to interact with the underlying host system. | ||
|
|
||
| use crate::{error::ExecutionError, input::Input, output::Output}; | ||
|
|
||
| use async_trait::async_trait; | ||
| use itertools::Itertools; | ||
| use slog::{debug, info, Logger}; | ||
| use std::io::{Read, Write}; | ||
| use std::process::Command; | ||
| use std::str::from_utf8; | ||
| use std::sync::Arc; | ||
|
|
||
| fn to_space_separated_string<T, I>(iter: T) -> String | ||
| where | ||
| T: IntoIterator<Item = I>, | ||
| I: std::fmt::Debug, | ||
| { | ||
| Itertools::intersperse( | ||
| iter.into_iter().map(|arg| format!("{arg:?}")), | ||
| " ".into(), | ||
| ) | ||
| .collect::<String>() | ||
| } | ||
|
|
||
| pub fn log_input(log: &Logger, id: u64, command: &Command) { | ||
| info!( | ||
| log, | ||
| "running command via executor"; "id" => id, "command" => %Input::from(command) | ||
| ); | ||
| debug!( | ||
| log, | ||
| "running command via executor"; "id" => id, "envs" => %to_space_separated_string(command.get_envs()) | ||
| ); | ||
| } | ||
|
|
||
| pub fn log_output(log: &Logger, id: u64, output: &Output) { | ||
| info!( | ||
| log, | ||
| "finished running command via executor"; | ||
| "id" => id, | ||
| "succeeded" => output.status.success(), | ||
| "status" => output.status.code() | ||
| ); | ||
| if !output.stdout.is_empty() { | ||
| debug!( | ||
| log, | ||
| "finished command stdout"; | ||
| "id" => id, | ||
| "stdout" => from_utf8(&output.stdout).unwrap_or("<Not valid UTF-8>"), | ||
| ); | ||
| } | ||
| if !output.stderr.is_empty() { | ||
| debug!( | ||
| log, | ||
| "finished command stderr"; | ||
| "id" => id, | ||
| "stderr" => from_utf8(&output.stderr).unwrap_or("<Not valid UTF-8>"), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// Describes the commonly-used "safe-to-reference" type describing the | ||
| /// Executor as a trait object. | ||
| pub type BoxedExecutor = Arc<dyn Executor>; | ||
|
|
||
| /// Describes an "executor", which can run [Command]s and return a response. | ||
| /// | ||
| /// - In production, this is usually `helios_protostar`'s executor. | ||
| /// - Under test, this can be customized, and `helios_tokamak`'s executor may be used. | ||
| #[async_trait] | ||
| pub trait Executor: Send + Sync { | ||
| /// Executes a task, waiting for it to complete, and returning output. | ||
| async fn execute_async( | ||
| &self, | ||
| command: &mut tokio::process::Command, | ||
| ) -> Result<Output, ExecutionError>; | ||
|
|
||
| /// Executes a task, waiting for it to complete, and returning output. | ||
| fn execute(&self, command: &mut Command) -> Result<Output, ExecutionError>; | ||
|
|
||
| /// Spawns a task, without waiting for it to complete. | ||
| fn spawn( | ||
| &self, | ||
| command: &mut Command, | ||
| ) -> Result<BoxedChild, ExecutionError>; | ||
| } | ||
|
|
||
| /// A wrapper around a spawned [Child] process. | ||
| pub type BoxedChild = Box<dyn Child>; | ||
|
|
||
| /// A child process spawned by the executor. | ||
| pub trait Child: Send { | ||
| /// Accesses the stdin of the spawned child, as a Writer. | ||
| fn take_stdin(&mut self) -> Option<Box<dyn Write + Send>>; | ||
|
|
||
| /// Accesses the stdout of the spawned child, as a Reader. | ||
| fn take_stdout(&mut self) -> Option<Box<dyn Read + Send>>; | ||
|
|
||
| /// Accesses the stderr of the spawned child, as a Reader. | ||
| fn take_stderr(&mut self) -> Option<Box<dyn Read + Send>>; | ||
|
|
||
| /// OS-assigned PID identifier for the child | ||
| fn id(&self) -> u32; | ||
|
|
||
| /// Waits for the child to complete, and returns the output. | ||
| fn wait(self: Box<Self>) -> Result<Output, ExecutionError>; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| use std::process::Command; | ||
|
|
||
| /// Wrapper around the input of a [std::process::Command] as strings. | ||
| #[derive(Clone, Debug, Eq, PartialEq)] | ||
| pub struct Input { | ||
| pub program: String, | ||
| pub args: Vec<String>, | ||
| pub envs: Vec<(String, String)>, | ||
| } | ||
|
|
||
| impl Input { | ||
| pub fn new<S: Into<String>>(program: S, args: Vec<S>) -> Self { | ||
| Self { | ||
| program: program.into(), | ||
| args: args.into_iter().map(|s| s.into()).collect(), | ||
| envs: vec![], | ||
| } | ||
| } | ||
|
|
||
| /// Short-hand for a whitespace-separated string, which can be provided | ||
| /// "like a shell command". | ||
| pub fn shell<S: AsRef<str>>(input: S) -> Self { | ||
| let mut args = shlex::split(input.as_ref()).expect("Invalid input"); | ||
|
|
||
| if args.is_empty() { | ||
| panic!("Empty input is invalid"); | ||
| } | ||
|
|
||
| Self::new(args.remove(0), args) | ||
| } | ||
| } | ||
|
|
||
| impl std::fmt::Display for Input { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "{}", shlex::quote(&self.program))?; | ||
| for arg in &self.args { | ||
| write!(f, " {}", shlex::quote(arg))?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| fn os_str_to_string(s: &std::ffi::OsStr) -> String { | ||
| s.to_string_lossy().to_string() | ||
| } | ||
|
|
||
| impl From<&Command> for Input { | ||
| fn from(command: &Command) -> Self { | ||
| Self { | ||
| program: os_str_to_string(command.get_program()), | ||
| args: command.get_args().map(os_str_to_string).collect(), | ||
| envs: command | ||
| .get_envs() | ||
| .map(|(k, v)| { | ||
| ( | ||
| os_str_to_string(k), | ||
| os_str_to_string(v.unwrap_or_default()), | ||
| ) | ||
| }) | ||
| .collect(), | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| //! Interfaces used to interact with the underlying host system. | ||
|
|
||
| mod error; | ||
| mod executor; | ||
| mod input; | ||
| mod output; | ||
|
|
||
| pub use error::*; | ||
| pub use executor::*; | ||
| pub use input::*; | ||
| pub use output::*; | ||
|
|
||
| pub const PFEXEC: &str = "/usr/bin/pfexec"; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.