Add environment as new output session type and change defaults to environment for CLI (#98)

pull/104/head
Chip Senkbeil 2 years ago committed by GitHub
parent 3794466dd0
commit f46eeea8d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,12 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- New `environment` session type that prints out environment variable
definitions for use in an interactive session or to evaluate
- Shell support introduced for ssh & distant servers, including a new shell
command for distant cli
- Support for JSON communication of ssh auth during launch (cli)
- Add windows and unix metadata files to overall metadata response data
### Changed
- Default session type for CLI (launch, action, etc) is `environment`
- Replace cbor library with alternative as old cbor lib has been abandoned
- Refactor some request & response types to work with new cbor lib
- Updated cli to always include serde dependency
@ -27,7 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Github actions no longer use paths-filter so every PR & commit will test
everything
## [0.15.1] - 2021-11-15
### Added
- `--key-from-stdin` option to listen cli command to read key from stdin

94
Cargo.lock generated

@ -409,6 +409,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.1"
@ -418,6 +424,40 @@ dependencies = [
"libc",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
@ -518,6 +558,7 @@ dependencies = [
"serde_json",
"structopt",
"strum",
"sysinfo",
"terminal_size",
"termwiz",
"tokio",
@ -1004,9 +1045,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.103"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
[[package]]
name = "libssh-rs"
@ -1105,6 +1146,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.7.13"
@ -1604,6 +1654,31 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -2012,6 +2087,21 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "sysinfo"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d82ade9d6621d4ca052a00bb6ea9ed513d223cba75a84625c5e9c0698ab6f5"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
"libc",
"ntapi",
"once_cell",
"rayon",
"winapi",
]
[[package]]
name = "tempfile"
version = "3.2.0"

@ -27,6 +27,7 @@ ssh2 = ["distant-ssh2"]
derive_more = { version = "0.99.16", default-features = false, features = ["display", "from", "error", "is_variant"] }
distant-core = { version = "=0.16.0", path = "distant-core", features = ["structopt"] }
flexi_logger = "0.18.0"
indoc = "1.0.3"
log = "0.4.14"
once_cell = "1.8.0"
rand = { version = "0.8.4", features = ["getrandom"] }
@ -34,6 +35,7 @@ serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
structopt = "0.3.22"
strum = { version = "0.21.0", features = ["derive"] }
sysinfo = "0.23.2"
tokio = { version = "1.12.0", features = ["full"] }
terminal_size = "0.1.17"
termwiz = "0.15.0"
@ -45,6 +47,9 @@ distant-ssh2 = { version = "=0.16.0", path = "distant-ssh2", features = ["serde"
[target.'cfg(unix)'.dependencies]
fork = "0.1.18"
# [target.'cfg(windows)'.dependencies]
# sysinfo = "0.23.2"
[dev-dependencies]
assert_cmd = "2.0.0"
assert_fs = "1.0.4"

@ -68,17 +68,22 @@ Launch a remote instance of `distant`. Calling `launch` will do the following:
1. Ssh into the specified host (in the below example, `my.example.com`)
2. Execute `distant listen --host ssh` on the remote machine
3. Receive on the local machine the credentials needed to connect to the server
4. Depending on the options specified, store/use the session settings so
4. Depending on the options specified, print/store/use the session settings so
future calls to `distant action` can connect
```bash
# Connects to my.example.com on port 22 via SSH to start a new session
# and print out information to configure your system to talk to it
distant launch my.example.com
# NOTE: If you are using sh, bash, or zsh, you can automatically set the
appropriate environment variables using the following
eval "$(distant launch my.example.com)"
# After the session is established, you can perform different operations
# on the remote machine via `distant action {command} [args]`
distant action copy path/to/file new/path/to/file
distant action run -- echo 'Hello, this is from the other side'
distant action spawn -- echo 'Hello, this is from the other side'
```
## License

@ -137,6 +137,11 @@ impl SessionInfo {
Ok(SocketAddr::from((addr, self.port)))
}
/// Converts the session's key to a hex string
pub fn key_to_unprotected_string(&self) -> String {
self.key.unprotected_to_hex_key()
}
/// Converts to unprotected string that exposes the key in the form of
/// `DISTANT CONNECT <host> <port> <key>`
pub fn to_unprotected_string(&self) -> String {

@ -0,0 +1,142 @@
use distant_core::SessionInfo;
use std::{ffi::OsStr, path::Path};
/// Prints out shell-specific environment information
pub fn print_environment(info: &SessionInfo) {
inner_print_environment(&info.host, info.port, &info.key_to_unprotected_string())
}
/// Prints out shell-specific environment information
#[cfg(unix)]
fn inner_print_environment(host: &str, port: u16, key: &str) {
match parent_exe_name() {
// If shell is csh or tcsh, we want to print differently
Some(s) if s.eq_ignore_ascii_case("csh") || s.eq_ignore_ascii_case("tcsh") => {
formatter::print_csh_string(host, port, key)
}
// If shell is fish, we want to print differently
Some(s) if s.eq_ignore_ascii_case("fish") => formatter::print_fish_string(host, port, key),
// Otherwise, we assume that the shell is compatible with sh (e.g. bash, dash, zsh)
_ => formatter::print_sh_string(host, port, key),
}
}
/// Prints out shell-specific environment information
#[cfg(windows)]
fn inner_print_environment(host: &str, port: u16, key: &str) {
match parent_exe_name() {
// If shell is powershell, we want to print differently
Some(s) if s.eq_ignore_ascii_case("powershell") => {
formatter::print_powershell_string(host, port, key)
}
// Otherwise, we assume that the shell was cmd.exe
_ => formatter::print_cmd_exe_string(host, port, key),
}
}
/// Retrieve the name of the parent process that spawned us
fn parent_exe_name() -> Option<String> {
use sysinfo::{Pid, PidExt, Process, ProcessExt, System, SystemExt};
let mut system = System::new();
// Get our own process pid
let pid = Pid::from_u32(std::process::id());
// Update our system's knowledge about our process
system.refresh_process(pid);
// Get our parent process' pid and update sustem's knowledge about parent process
let maybe_parent_pid = system.process(pid).and_then(Process::parent);
if let Some(pid) = maybe_parent_pid {
system.refresh_process(pid);
}
maybe_parent_pid
.and_then(|pid| system.process(pid))
.map(Process::exe)
.and_then(Path::file_name)
.map(OsStr::to_string_lossy)
.map(|s| s.to_string())
}
mod formatter {
use indoc::printdoc;
/// Prints out a {csh,tcsh}-specific example of setting environment variables
#[cfg(unix)]
pub fn print_csh_string(host: &str, port: u16, key: &str) {
printdoc! {r#"
setenv DISTANT_HOST "{host}"
setenv DISTANT_PORT "{port}"
setenv DISTANT_KEY "{key}"
"#,
host = host,
port = port,
key = key,
}
}
/// Prints out a fish-specific example of setting environment variables
#[cfg(unix)]
pub fn print_fish_string(host: &str, port: u16, key: &str) {
printdoc! {r#"
# Please export the following variables to use with actions
set -gx DISTANT_HOST {host}
set -gx DISTANT_PORT {port}
set -gx DISTANT_KEY {key}
"#,
host = host,
port = port,
key = key,
}
}
/// Prints out an sh-compliant example of setting environment variables
#[cfg(unix)]
pub fn print_sh_string(host: &str, port: u16, key: &str) {
printdoc! {r#"
# Please export the following variables to use with actions
export DISTANT_HOST="{host}"
export DISTANT_PORT="{port}"
export DISTANT_KEY="{key}"
"#,
host = host,
port = port,
key = key,
}
}
/// Prints out a powershell example of setting environment variables
#[cfg(windows)]
pub fn print_powershell_string(host: &str, port: u16, key: &str) {
printdoc! {r#"
# Please export the following variables to use with actions
$Env:DISTANT_HOST = "{host}"
$Env:DISTANT_PORT = "{port}"
$Env:DISTANT_KEY = "{key}"
"#,
host = host,
port = port,
key = key,
}
}
/// Prints out a command prompt example of setting environment variables
#[cfg(windows)]
pub fn print_cmd_exe_string(host: &str, port: u16, key: &str) {
printdoc! {r#"
REM Please export the following variables to use with actions
SET DISTANT_HOST="{host}"
SET DISTANT_PORT="{port}"
SET DISTANT_KEY="{key}"
"#,
host = host,
port = port,
key = key,
}
}
}

@ -1,5 +1,6 @@
mod buf;
mod constants;
mod environment;
mod exit;
mod link;
mod msg;

@ -359,6 +359,17 @@ impl FromStr for BindAddress {
)]
#[strum(serialize_all = "snake_case")]
pub enum SessionOutput {
/// Session will be exposed as a series of environment variables
///
/// * `DISTANT_HOST=<host>`
/// * `DISTANT_PORT=<port>`
/// * `DISTANT_KEY=<key>`
///
/// Note that this does not actually create the environment variables,
/// but instead prints out a message detailing how to set the environment
/// variables, which can be evaluated to set them
Environment,
/// Session is in a file in the form of `DISTANT CONNECT <host> <port> <key>`
File,
@ -378,16 +389,9 @@ pub enum SessionOutput {
}
impl Default for SessionOutput {
/// For unix-based systems, output defaults to a socket
#[cfg(unix)]
/// Default to environment output
fn default() -> Self {
Self::Socket
}
/// For non-unix-based systems, output defaults to a file
#[cfg(not(unix))]
fn default() -> Self {
Self::File
Self::Environment
}
}
@ -431,16 +435,9 @@ pub enum SessionInput {
}
impl Default for SessionInput {
/// For unix-based systems, input defaults to a socket
#[cfg(unix)]
fn default() -> Self {
Self::Socket
}
/// For non-unix-based systems, input defaults to a file
#[cfg(not(unix))]
/// Default to environment output
fn default() -> Self {
Self::File
Self::Environment
}
}

@ -1,4 +1,5 @@
use crate::{
environment,
exit::{ExitCode, ExitCodeError},
msg::{MsgReceiver, MsgSender},
opt::{CommonOpt, Format, LaunchSubcommand, SessionOutput},
@ -51,6 +52,10 @@ pub fn run(cmd: LaunchSubcommand, opt: CommonOpt) -> Result<(), Error> {
// Handle sharing resulting session in different ways
match session_output {
SessionOutput::Environment => {
debug!("Outputting session to environment");
environment::print_environment(&session)
}
SessionOutput::File => {
debug!("Outputting session to {:?}", session_file);
rt.block_on(async { SessionInfoFile::new(session_file, session).save().await })?

Loading…
Cancel
Save