feat(config): land the wave-3 per-feature family-seam handles (w3-seam-config)
- ServeConfig gains cfg-gated SessionSeam / GitSeam Option fields (whole-Option struct per seam — the shape decision recorded in the task notes): unset → the owned rows decline even though the feature is on, no construction-time validation - SessionSeam carries the alktty backends map + backend selector + ownership provider; GitSeam carries the alkgit backend quartet + caller-owned pipeline semaphore + push_options_negotiated - optional deps alktty 0.8.1 / alkgit 0.1.0 wired feature-to-dep (pins verified against crates.io: licenses, MSRV, shared alkcall resolution); gix-hash 0.28 as dev-dep only (alkgit does not re-export ObjectId for the trait doubles) - per-feature construction/unset-declines/Debug rows in config.rs; accessors session_seam_configured / git_seam_configured; docs state what each seam handle carries Verified: cargo test (per-feature + --all-features), clippy ×2 (-D warnings), fmt, cargo doc --no-deps, cargo tree feature isolation (git-build: no alktty; session-build: no alkgit), taskgraph validate
This commit is contained in:
1 parent
d1f755f2d9
commit
270365ea4c
5 files changed
+1351
-21
No files matched your search
+429
-5
@@ -8,8 +8,12 @@
|
||||
//! - [`ServeConfig`] — the server direction's assembly-supplied serving
|
||||
//! configuration: the OQ-08 fields ([`ServeConfig::shell_argv`] /
|
||||
//! [`ServeConfig::exec_wrapper_argv`]), the injected identity,
|
||||
//! host-key and tunnel handles, the OQ-09 `cwd`/`env` values, and the
|
||||
//! [`Limits`].
|
||||
//! host-key and tunnel handles, the per-feature family-seam handles
|
||||
//! (the wave-3 extension — [`SessionSeam`] carries the alktty
|
||||
//! backends map + backend selector + ownership provider;
|
||||
//! [`GitSeam`] carries the alkgit backend quartet + the caller-owned
|
||||
//! pipeline + the push-options flag), the OQ-09 `cwd`/`env` values,
|
||||
//! and the [`Limits`].
|
||||
//! - [`DialConfig`] — the client direction's dial configuration
|
||||
//! (`client.md` §The dial surface): address, key source, host-key
|
||||
//! policy, and the bounded-resource knobs (carried by [`Limits`]).
|
||||
@@ -22,7 +26,14 @@
|
||||
//! request time (wave-2/3 consumption), never a fallback, never
|
||||
//! construction-time validation (`error.rs`'s no-config-error
|
||||
//! contract). No construction-time validation exists in this
|
||||
//! module.
|
||||
//! module. The wave-3 seam handles extend the same rule per
|
||||
//! feature: [`ServeConfig::session_seam`] unset → the session rows
|
||||
//! decline even though the `session` feature is on;
|
||||
//! [`ServeConfig::git_seam`] unset → the git row declines even
|
||||
//! though `git` is on (the decline matrix's config-driven notes;
|
||||
//! the feature alone configures nothing). The tunnel handle stays
|
||||
//! the flat `Option<Arc<ChannelClient>>` as landed — a single-field
|
||||
//! seam needs no struct.
|
||||
//! - **Injected handles, never crate-resolved identity or trust**:
|
||||
//! the [`IdentityProvider`], [`HostKeySource`] and
|
||||
//! [`HostKeyPolicy`] handles are `Arc<dyn ..>` the assembly
|
||||
@@ -44,12 +55,20 @@
|
||||
//! budget model.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
#[cfg(feature = "session")]
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use alkcall::channels::client::ChannelClient;
|
||||
use alkcall::core::auth::IdentityProvider;
|
||||
#[cfg(feature = "session")]
|
||||
use alkcall::core::ownership::OwnershipProvider;
|
||||
#[cfg(feature = "git")]
|
||||
use alkgit::{GitPackGen, GitPackIngest, GitRefs, GitRegistry};
|
||||
#[cfg(feature = "session")]
|
||||
use alktty::TtyBackend;
|
||||
|
||||
use crate::host_key::HostKeyPolicy;
|
||||
use crate::key_source::{HostKeySource, KeySource};
|
||||
@@ -172,6 +191,101 @@ impl Default for Limits {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `session` feature's alktty seam handle (`mapping.md` §The
|
||||
/// seams): what `drive_session[_pre_negotiated]` takes that the
|
||||
/// assembly composes (alktty 0.8.1's `adapter.rs:205-241` signature).
|
||||
/// **Shape decision** (recorded at `w3-seam-config`): a whole-`Option`
|
||||
/// struct per seam, not flat `Option` fields — each seam is consumed
|
||||
/// atomically by exactly one seam call, so a struct admits no
|
||||
/// half-supplied seam states and the unset-declines contract reads as
|
||||
/// one `Option`: **`ServeConfig::session_seam` unset → the session
|
||||
/// rows decline even though the `session` feature is on** (the
|
||||
/// decline matrix's config-driven note), checked at request time by
|
||||
/// the mappings — no construction-time validation. Cloned into each
|
||||
/// spawned serving task (all payload types `Arc` or plain data).
|
||||
#[cfg(feature = "session")]
|
||||
#[derive(Clone)]
|
||||
pub struct SessionSeam {
|
||||
/// The alktty backends map — the `TtyAdapter`-style registry keyed
|
||||
/// by the `NegotiateRequest.backend` selector string. **Assembled
|
||||
/// by the assembly**: alkssh never touches a real backend, and the
|
||||
/// `local` backend shapes stay alktty-side (this dep's `local`
|
||||
/// feature is not enabled — the map holds assembly-supplied
|
||||
/// implementations of alktty's [`TtyBackend`] only).
|
||||
pub backends: Arc<HashMap<String, Arc<dyn TtyBackend>>>,
|
||||
/// The backend **selector** — the `NegotiateRequest.backend`
|
||||
/// string alkssh writes into the locally-constructed negotiation
|
||||
/// (`w3-tty-session`'s negotiation rows; the wire command/args
|
||||
/// never reach it). Assembly-supplied, never wire-derived.
|
||||
pub backend: String,
|
||||
/// The ADR-050 ownership provider — passed through
|
||||
/// `drive_session[_pre_negotiated]` verbatim (the family ACL
|
||||
/// layer riding alongside; an alkcall type, no new dep).
|
||||
pub ownership: Option<Arc<dyn OwnershipProvider>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "session")]
|
||||
impl std::fmt::Debug for SessionSeam {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut keys: Vec<&String> = self.backends.keys().collect();
|
||||
keys.sort();
|
||||
f.debug_struct("SessionSeam")
|
||||
.field("backend_keys", &keys)
|
||||
.field("backend", &self.backend)
|
||||
.field("ownership_set", &self.ownership.is_some())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The `git` feature's alkgit seam handle (`mapping.md` §The seams):
|
||||
/// what `serve_duplex_resolved` takes that the assembly composes
|
||||
/// (alkgit 0.1.0's `duplex.rs:1412-1426` signature — the four backend
|
||||
/// traits, the caller-owned pipeline, the push-options gate). Same
|
||||
/// whole-`Option`-struct decision as [`SessionSeam`]:
|
||||
/// **`ServeConfig::git_seam` unset → the git row declines even though
|
||||
/// the `git` feature is on**, checked at request time by the
|
||||
/// mappings — no construction-time validation.
|
||||
#[cfg(feature = "git")]
|
||||
#[derive(Clone)]
|
||||
pub struct GitSeam {
|
||||
/// The repo-id resolution trait (`GitRegistry::resolve`) — the
|
||||
/// ADR-007 pre-protocol gate's other half; alkssh never resolves
|
||||
/// (the collapsed-generic-denial posture lives in alkgit).
|
||||
pub registry: Arc<dyn GitRegistry>,
|
||||
/// The ref listing + CAS transaction trait
|
||||
/// (`list_refs`/`apply_updates`).
|
||||
pub refs: Arc<dyn GitRefs>,
|
||||
/// The pack generation + negotiation-ack trait
|
||||
/// (`generate`/`common_haves`).
|
||||
pub packgen: Arc<dyn GitPackGen>,
|
||||
/// The pack-preparation trait (`prepare` — never applies; the
|
||||
/// `GitRefs::apply_updates` CAS stays the single ingestion home).
|
||||
pub ingest: Arc<dyn GitPackIngest>,
|
||||
/// The assembler's push-options flag (alkgit's ADR-013 §11 gate,
|
||||
/// passed through verbatim) — `false` in the v1 default.
|
||||
pub push_options_negotiated: bool,
|
||||
/// The caller-owned, server-wide pipeline permit — alkgit's
|
||||
/// pack-pipeline concurrency bound, *shared across every session
|
||||
/// the serving direction takes* (the caller keeps its own handle;
|
||||
/// `Arc` for the share). One per `DialConfig`-independent serve
|
||||
/// direction, never per channel.
|
||||
pub pipeline: Arc<tokio::sync::Semaphore>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
impl std::fmt::Debug for GitSeam {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("GitSeam")
|
||||
.field("registry", &"<dyn GitRegistry>")
|
||||
.field("refs", &"<dyn GitRefs>")
|
||||
.field("packgen", &"<dyn GitPackGen>")
|
||||
.field("ingest", &"<dyn GitPackIngest>")
|
||||
.field("push_options_negotiated", &self.push_options_negotiated)
|
||||
.field("pipeline_permits", &self.pipeline.available_permits())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The server direction's assembly-supplied serving configuration
|
||||
/// (`overview.md` §Server stack): everything wave-2's serving loop
|
||||
/// takes at construction. Pure data + injected handles — no russh
|
||||
@@ -215,6 +329,22 @@ pub struct ServeConfig {
|
||||
/// `NegotiateRequest.env` — **never wire-derived**. Empty by
|
||||
/// default.
|
||||
pub session_env: BTreeMap<String, String>,
|
||||
/// The `session` feature's alktty seam handle ([`SessionSeam`] —
|
||||
/// the backends map + backend selector + ownership provider the
|
||||
/// `drive_session[_pre_negotiated]` rows take). **Unset → the
|
||||
/// session rows decline even though the `session` feature is on**
|
||||
/// (the decline matrix's config-driven note; the feature alone
|
||||
/// configures nothing). Wave-3 extension of the wave-1 config
|
||||
/// surface — shape decision recorded on [`SessionSeam`].
|
||||
#[cfg(feature = "session")]
|
||||
pub session_seam: Option<SessionSeam>,
|
||||
/// The `git` feature's alkgit seam handle ([`GitSeam`] — the four
|
||||
/// backend traits + the caller-owned pipeline + the push-options
|
||||
/// flag the `serve_duplex_resolved` row takes). **Unset → the git
|
||||
/// row declines even though the `git` feature is on** (same
|
||||
/// postures as [`ServeConfig::session_seam`]).
|
||||
#[cfg(feature = "git")]
|
||||
pub git_seam: Option<GitSeam>,
|
||||
/// The injected identity provider (alkcall's
|
||||
/// `IdentityProvider::resolve_from_fingerprint`) — the assembly
|
||||
/// supplies it; the crate never resolves identities itself and
|
||||
@@ -255,6 +385,10 @@ impl ServeConfig {
|
||||
exec_wrapper_argv: None,
|
||||
session_cwd: None,
|
||||
session_env: BTreeMap::new(),
|
||||
#[cfg(feature = "session")]
|
||||
session_seam: None,
|
||||
#[cfg(feature = "git")]
|
||||
git_seam: None,
|
||||
identity_provider,
|
||||
host_key_source,
|
||||
channel_client: None,
|
||||
@@ -284,17 +418,47 @@ impl ServeConfig {
|
||||
pub fn tunnel_configured(&self) -> bool {
|
||||
self.channel_client.is_some()
|
||||
}
|
||||
|
||||
/// Whether the session rows' family seam is configured in this
|
||||
/// configuration ([`SessionSeam`] present; the `session` feature
|
||||
/// gate applies at the dispatch on top): `false` means the rows
|
||||
/// decline — the unset-declines contract, checked at request time
|
||||
/// by the wave-3 mappings, never here.
|
||||
#[cfg(feature = "session")]
|
||||
pub fn session_seam_configured(&self) -> bool {
|
||||
self.session_seam.is_some()
|
||||
}
|
||||
|
||||
/// Whether the git row's family seam is configured in this
|
||||
/// configuration ([`GitSeam`] present; the `git` feature gate
|
||||
/// applies at the dispatch on top): `false` means the row declines
|
||||
/// — the unset-declines contract, checked at request time by the
|
||||
/// wave-3 mappings, never here.
|
||||
#[cfg(feature = "git")]
|
||||
pub fn git_seam_configured(&self) -> bool {
|
||||
self.git_seam.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ServeConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ServeConfig")
|
||||
let mut debug = f.debug_struct("ServeConfig");
|
||||
debug
|
||||
.field("shell_argv", &self.shell_argv)
|
||||
.field("exec_wrapper_argv", &self.exec_wrapper_argv)
|
||||
.field("session_cwd", &self.session_cwd)
|
||||
.field("session_env", &self.session_env)
|
||||
.field("identity_provider", &"<dyn IdentityProvider>")
|
||||
.field("host_key_source", &"<dyn HostKeySource>")
|
||||
.field("host_key_source", &"<dyn HostKeySource>");
|
||||
#[cfg(feature = "session")]
|
||||
{
|
||||
debug.field("session_seam", &self.session_seam.is_some());
|
||||
}
|
||||
#[cfg(feature = "git")]
|
||||
{
|
||||
debug.field("git_seam", &self.git_seam.is_some());
|
||||
}
|
||||
debug
|
||||
.field("channel_client", &self.channel_client.as_ref().is_some())
|
||||
.field("limits", &self.limits)
|
||||
.finish()
|
||||
@@ -503,6 +667,169 @@ mod tests {
|
||||
assert_eq!(serve.session_env.len(), 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
mod git_seam {
|
||||
use alkgit::{
|
||||
GitPackGen, GitPackIngest, GitRefs, GitRegistry, PreparedPush, PushOptions, RefLine,
|
||||
RefOutcome, RefUpdate, RegistryError, RepoRecord, StorageError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use gix_hash::ObjectId;
|
||||
|
||||
use super::super::{GitSeam, ServeConfig};
|
||||
use super::*;
|
||||
|
||||
struct NoopRegistry;
|
||||
|
||||
#[async_trait]
|
||||
impl GitRegistry for NoopRegistry {
|
||||
async fn resolve(&self, _repo_id: &str) -> Result<RepoRecord, RegistryError> {
|
||||
Err(RegistryError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
struct NoopRefs;
|
||||
|
||||
#[async_trait]
|
||||
impl GitRefs for NoopRefs {
|
||||
async fn list_refs(&self, _repo: &RepoRecord) -> Result<Vec<RefLine>, StorageError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn apply_updates(
|
||||
&self,
|
||||
_repo: &RepoRecord,
|
||||
_updates: Vec<RefUpdate>,
|
||||
_atomic: bool,
|
||||
) -> Result<Vec<RefOutcome>, StorageError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
struct NoopPackGen;
|
||||
|
||||
#[async_trait]
|
||||
impl GitPackGen for NoopPackGen {
|
||||
async fn generate(
|
||||
&self,
|
||||
_repo: &RepoRecord,
|
||||
_wants: Vec<ObjectId>,
|
||||
_boundary_haves: Vec<ObjectId>,
|
||||
_limits: alkgit::Limits,
|
||||
_sink: Box<dyn std::io::Write + Send>,
|
||||
) -> Result<(), StorageError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn common_haves(
|
||||
&self,
|
||||
_repo: &RepoRecord,
|
||||
_haves: Vec<ObjectId>,
|
||||
) -> Result<Vec<ObjectId>, StorageError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
struct NoopPackIngest;
|
||||
|
||||
#[async_trait]
|
||||
impl GitPackIngest for NoopPackIngest {
|
||||
async fn prepare(
|
||||
&self,
|
||||
_repo: &RepoRecord,
|
||||
_updates: Vec<RefUpdate>,
|
||||
_pack: Box<dyn std::io::Read + Send>,
|
||||
_push_options: Option<PushOptions>,
|
||||
_limits: alkgit::Limits,
|
||||
) -> Result<PreparedPush, StorageError> {
|
||||
Ok(PreparedPush {
|
||||
unpack: Ok(()),
|
||||
updates: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn assembled_seam() -> GitSeam {
|
||||
GitSeam {
|
||||
registry: Arc::new(NoopRegistry),
|
||||
refs: Arc::new(NoopRefs),
|
||||
packgen: Arc::new(NoopPackGen),
|
||||
ingest: Arc::new(NoopPackIngest),
|
||||
push_options_negotiated: false,
|
||||
pipeline: Arc::new(tokio::sync::Semaphore::new(2)),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_config_git_seam_unset_declines_though_the_feature_is_on() {
|
||||
let serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
|
||||
assert!(serve.git_seam.is_none());
|
||||
assert!(!serve.git_seam_configured());
|
||||
|
||||
let debug = format!("{serve:?}");
|
||||
assert!(debug.contains("git_seam: false"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_config_holds_the_git_seam_whole_and_cloneable() {
|
||||
let mut serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
let seam = assembled_seam();
|
||||
let registry = Arc::clone(&seam.registry);
|
||||
|
||||
serve.git_seam = Some(seam);
|
||||
|
||||
assert!(serve.git_seam_configured());
|
||||
let held = serve.git_seam.as_ref().expect("the seam holds");
|
||||
assert!(Arc::ptr_eq(®istry, &held.registry));
|
||||
assert!(!held.push_options_negotiated);
|
||||
assert_eq!(held.pipeline.available_permits(), 2);
|
||||
|
||||
let cloned = held.clone();
|
||||
assert!(Arc::ptr_eq(&cloned.registry, ®istry));
|
||||
assert!(!cloned.push_options_negotiated);
|
||||
assert_eq!(cloned.pipeline.available_permits(), 2);
|
||||
|
||||
let debug = format!("{serve:?}");
|
||||
assert!(debug.contains("git_seam: true"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_seam_pipeline_is_the_single_server_wide_share() {
|
||||
let mut serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
let pipeline = Arc::new(tokio::sync::Semaphore::new(2));
|
||||
|
||||
let mut seam = assembled_seam();
|
||||
seam.pipeline = Arc::clone(&pipeline);
|
||||
serve.git_seam = Some(seam);
|
||||
let held = serve.git_seam.as_ref().expect("the seam holds");
|
||||
let cloned = held.clone();
|
||||
|
||||
assert!(Arc::ptr_eq(&pipeline, &held.pipeline));
|
||||
assert!(Arc::ptr_eq(&pipeline, &cloned.pipeline));
|
||||
|
||||
let _permit = cloned
|
||||
.pipeline
|
||||
.try_acquire()
|
||||
.expect("a permit is available");
|
||||
assert_eq!(held.pipeline.available_permits(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_seam_debug_shapes_itself_not_the_backends() {
|
||||
let seam = assembled_seam();
|
||||
let debug = format!("{seam:?}");
|
||||
|
||||
assert!(debug.contains("GitSeam"));
|
||||
assert!(debug.contains("<dyn GitRegistry>"));
|
||||
assert!(debug.contains("<dyn GitRefs>"));
|
||||
assert!(debug.contains("<dyn GitPackGen>"));
|
||||
assert!(debug.contains("<dyn GitPackIngest>"));
|
||||
assert!(debug.contains("push_options_negotiated: false"));
|
||||
assert!(debug.contains("pipeline_permits: 2"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dial_config_defaults_carry_the_keepalive_posture() {
|
||||
let dial = DialConfig::new("runner.example:22", key_source(), Arc::new(RejectAll));
|
||||
@@ -528,6 +855,103 @@ mod tests {
|
||||
assert!(Arc::ptr_eq(&policy, &dial.host_key_policy));
|
||||
}
|
||||
|
||||
// The wave-3 per-feature seam handles: the construction shapes, the
|
||||
// unset-declines-preserved rows and the Debug posture — each block
|
||||
// only runs in the build that carries its feature (compile-time
|
||||
// feature isolation: no git build ever touches alktty types,
|
||||
// no session build ever touches alkgit types).
|
||||
#[cfg(feature = "session")]
|
||||
mod session_seam {
|
||||
use alktty::{TtyBackend, TtyError, TtyHandle, TtyParams};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::super::{ServeConfig, SessionSeam};
|
||||
use super::*;
|
||||
|
||||
struct StubBackend;
|
||||
|
||||
#[async_trait]
|
||||
impl TtyBackend for StubBackend {
|
||||
async fn allocate(&self, _params: &TtyParams) -> Result<TtyHandle, TtyError> {
|
||||
Err(TtyError::AllocFailed {
|
||||
message: "stub".into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn assembled_seam() -> SessionSeam {
|
||||
let mut backends = std::collections::HashMap::new();
|
||||
backends.insert("stub".to_string(), Arc::new(StubBackend) as _);
|
||||
SessionSeam {
|
||||
backends: Arc::new(backends),
|
||||
backend: "stub".to_string(),
|
||||
ownership: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_config_session_seam_unset_declines_though_the_feature_is_on() {
|
||||
let serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
|
||||
assert!(serve.session_seam.is_none());
|
||||
assert!(!serve.session_seam_configured());
|
||||
|
||||
let debug = format!("{serve:?}");
|
||||
assert!(debug.contains("session_seam: false"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_config_holds_the_session_seam_whole_and_cloneable() {
|
||||
let mut serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
let seam = assembled_seam();
|
||||
let backends = Arc::clone(&seam.backends);
|
||||
|
||||
serve.session_seam = Some(seam);
|
||||
|
||||
assert!(serve.session_seam_configured());
|
||||
let held = serve.session_seam.as_ref().expect("the seam holds");
|
||||
assert!(Arc::ptr_eq(&backends, &held.backends));
|
||||
assert_eq!(held.backend, "stub");
|
||||
assert!(held.ownership.is_none());
|
||||
|
||||
let cloned = held.clone();
|
||||
assert!(Arc::ptr_eq(&cloned.backends, &backends));
|
||||
assert_eq!(cloned.backend, "stub");
|
||||
|
||||
let debug = format!("{serve:?}");
|
||||
assert!(debug.contains("session_seam: true"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_seam_configure_is_one_option_independent_of_the_oq08_fields() {
|
||||
let mut serve = ServeConfig::new(Arc::new(NoopIdentityProvider), host_key_source());
|
||||
serve.session_seam = Some(assembled_seam());
|
||||
|
||||
assert!(serve.session_seam_configured());
|
||||
assert!(!serve.shell_configured());
|
||||
assert!(!serve.exec_wrapper_configured());
|
||||
|
||||
serve.shell_argv = Some(vec!["/bin/sh".to_string(), "-l".to_string()]);
|
||||
assert!(serve.shell_configured());
|
||||
assert!(serve.session_seam_configured());
|
||||
|
||||
serve.session_seam = None;
|
||||
assert!(serve.shell_configured());
|
||||
assert!(!serve.session_seam_configured());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_seam_debug_shapes_itself_not_the_backends() {
|
||||
let seam = assembled_seam();
|
||||
let debug = format!("{seam:?}");
|
||||
|
||||
assert!(debug.contains("SessionSeam"));
|
||||
assert!(debug.contains(r#"backend_keys: ["stub"]"#));
|
||||
assert!(debug.contains(r#"backend: "stub""#));
|
||||
assert!(debug.contains("ownership_set: false"));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn dial_config_key_source_stays_fail_closed_through_the_config() {
|
||||
let dial = DialConfig::new("fleet.example:22", key_source(), Arc::new(RejectAll));
|
||||
|
||||
@@ -36,6 +36,10 @@ mod key_source;
|
||||
mod serve;
|
||||
|
||||
pub use adapter::SshAdapter;
|
||||
#[cfg(feature = "git")]
|
||||
pub use config::GitSeam;
|
||||
#[cfg(feature = "session")]
|
||||
pub use config::SessionSeam;
|
||||
pub use config::{DialConfig, Limits, ServeConfig};
|
||||
pub use error::{AdapterError, KeySourceError};
|
||||
pub use fingerprint::fingerprint;
|
||||
|
||||
Reference in new issue
Block a user