huskies: merge 1182 bug Sled uplinks all register as 'workspace' — containers never get HUSKIES_PROJECT_NAME

This commit is contained in:
Huskies Agent
2026-07-17 10:46:22 +00:00
parent ae47dd29e8
commit 1d71877af6
4 changed files with 84 additions and 0 deletions
@@ -763,6 +763,7 @@ async fn handle_adopt_project(
let mut docker_args = project_docker_run_args(
&container_name,
name,
port,
ssh_port,
&pubkey,
@@ -1150,6 +1151,7 @@ pub async fn handle_new_project(
// string that is returned to the caller or written to a log.
let mut docker_args = project_docker_run_args(
&container_name,
name,
port,
ssh_port,
&pubkey,
@@ -1361,6 +1363,7 @@ pub async fn build_project_image(
#[allow(clippy::too_many_arguments)]
pub(crate) fn project_docker_run_args(
container_name: &str,
project_name: &str,
port: u16,
ssh_port: u16,
pubkey: &str,
@@ -1389,6 +1392,8 @@ pub(crate) fn project_docker_run_args(
"-e".into(),
"HUSKIES_PORT=3001".into(),
"-e".into(),
format!("HUSKIES_PROJECT_NAME={project_name}"),
"-e".into(),
format!("HUSKIES_SSH_PUBKEY={pubkey}"),
"-e".into(),
format!("GIT_USER_NAME={git_user_name}"),
@@ -1839,6 +1844,7 @@ mod tests {
fn project_docker_args_include_huskies_host_env() {
let args = project_docker_run_args(
"huskies-myapp",
"myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
@@ -1887,10 +1893,33 @@ mod tests {
);
}
#[test]
fn project_docker_args_include_project_name_env() {
let args = project_docker_run_args(
"huskies-myapp",
"myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
"Test User",
"test@example.com",
None,
"",
);
let pairs: Vec<_> = args.windows(2).collect();
assert!(
pairs
.iter()
.any(|w| w[0] == "-e" && w[1] == "HUSKIES_PROJECT_NAME=myapp"),
"expected -e HUSKIES_PROJECT_NAME=myapp in docker args, got: {args:?}"
);
}
#[test]
fn project_docker_args_no_gateway_url_when_empty() {
let args = project_docker_run_args(
"huskies-myapp",
"myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
@@ -1910,6 +1939,7 @@ mod tests {
let creds = std::path::Path::new("/home/user/.claude/.credentials.json");
let args = project_docker_run_args(
"huskies-myapp",
"myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
@@ -1930,6 +1960,7 @@ mod tests {
fn project_docker_args_no_credentials_mount_when_none() {
let args = project_docker_run_args(
"huskies-myapp",
"myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
@@ -228,6 +228,7 @@ pub async fn handle_project_rebuild(
let mut docker_args = super::new_project::project_docker_run_args(
&container_name,
name,
port,
ssh_port,
&pubkey,
+1
View File
@@ -395,6 +395,7 @@ mod tests {
// code path that sets HUSKIES_GATEWAY_URL on the sled container.
let docker_args = project_docker_run_args(
"huskies-sled-relay",
"sled-relay",
3200,
2300,
"ssh-ed25519 AAAA...",
+51
View File
@@ -343,6 +343,12 @@ async fn run_sled_uplink_session(
"[gateway/sled-uplink] Sled '{}' connected and registered",
identity
);
if !state.projects.read().await.contains_key(&identity) {
crate::slog_warn!(
"[gateway/sled-uplink] Sled registered under name '{}', which matches no project in projects.toml",
identity
);
}
// Aggregator channel for perm responses produced by spawned per-request tasks.
let (agg_tx, mut agg_rx) =
@@ -734,4 +740,49 @@ mod tests {
"with no sled tokens configured anywhere, the uplink must accept unauthenticated"
);
}
#[tokio::test]
async fn unconfigured_project_name_logs_warning() {
let mut projects = BTreeMap::new();
projects.insert(
"other-project".to_string(),
ProjectEntry::with_url("http://other-project:3001"),
);
let config = GatewayConfig {
projects,
sled_tokens: BTreeMap::new(),
release_channels: BTreeMap::new(),
};
let (base_url, _state) = start_test_gateway(config).await;
let url = format!("{base_url}/api/sled-uplink?project=ghost-project");
let (mut ws_stream, _) = tokio_tungstenite::connect_async(url.as_str())
.await
.expect("uplink connect should succeed when no tokens are configured");
let identity = crate::sled_uplink::UplinkEnvelope {
msg_type: "identity".to_string(),
req_id: String::new(),
payload: serde_json::json!({ "project": "ghost-project" }),
};
ws_stream
.send(tokio_tungstenite::tungstenite::Message::Text(
serde_json::to_string(&identity).unwrap().into(),
))
.await
.unwrap();
// Give the server task time to process the identity frame and register.
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
let warnings = crate::log_buffer::global().get_recent(
50,
Some("ghost-project"),
Some(&crate::log_buffer::LogLevel::Warn),
);
assert!(
warnings.iter().any(|w| w.contains("matches no project")),
"expected an unconfigured-project warning, got: {warnings:?}"
);
}
}