huskies: merge 1130 story Adopted/launched project containers bind huskies to 127.0.0.1, unreachable from host MCP
This commit is contained in:
@@ -441,24 +441,17 @@ async fn handle_adopt_project(
|
|||||||
let container_url = format!("http://127.0.0.1:{port}");
|
let container_url = format!("http://127.0.0.1:{port}");
|
||||||
let container_name = format!("huskies-{name}");
|
let container_name = format!("huskies-{name}");
|
||||||
|
|
||||||
let mut docker_args: Vec<String> = vec![
|
let mut docker_args = project_docker_run_args(
|
||||||
"run".into(),
|
&container_name,
|
||||||
"-d".into(),
|
port,
|
||||||
"--name".into(),
|
ssh_port,
|
||||||
container_name.clone(),
|
&pubkey,
|
||||||
"-p".into(),
|
&git_user_name,
|
||||||
format!("127.0.0.1:{port}:3001"),
|
&git_user_email,
|
||||||
"-p".into(),
|
);
|
||||||
format!("127.0.0.1:{ssh_port}:22"),
|
|
||||||
"-e".into(),
|
docker_args.push("-v".into());
|
||||||
format!("HUSKIES_SSH_PUBKEY={pubkey}"),
|
docker_args.push(format!("{}:/workspace", host_path.display()));
|
||||||
"-e".into(),
|
|
||||||
format!("GIT_USER_NAME={git_user_name}"),
|
|
||||||
"-e".into(),
|
|
||||||
format!("GIT_USER_EMAIL={git_user_email}"),
|
|
||||||
"-v".into(),
|
|
||||||
format!("{}:/workspace", host_path.display()),
|
|
||||||
];
|
|
||||||
|
|
||||||
for mount in &ssh_key_mounts {
|
for mount in &ssh_key_mounts {
|
||||||
docker_args.push("-v".into());
|
docker_args.push("-v".into());
|
||||||
@@ -793,22 +786,14 @@ pub async fn handle_new_project(
|
|||||||
|
|
||||||
// Build the `docker run` argument list. The token must never appear in any
|
// Build the `docker run` argument list. The token must never appear in any
|
||||||
// string that is returned to the caller or written to a log.
|
// string that is returned to the caller or written to a log.
|
||||||
let mut docker_args: Vec<String> = vec![
|
let mut docker_args = project_docker_run_args(
|
||||||
"run".into(),
|
&container_name,
|
||||||
"-d".into(),
|
port,
|
||||||
"--name".into(),
|
ssh_port,
|
||||||
container_name.clone(),
|
&pubkey,
|
||||||
"-p".into(),
|
&git_user_name,
|
||||||
format!("127.0.0.1:{port}:3001"),
|
&git_user_email,
|
||||||
"-p".into(),
|
);
|
||||||
format!("127.0.0.1:{ssh_port}:22"),
|
|
||||||
"-e".into(),
|
|
||||||
format!("HUSKIES_SSH_PUBKEY={pubkey}"),
|
|
||||||
"-e".into(),
|
|
||||||
format!("GIT_USER_NAME={git_user_name}"),
|
|
||||||
"-e".into(),
|
|
||||||
format!("GIT_USER_EMAIL={git_user_email}"),
|
|
||||||
];
|
|
||||||
|
|
||||||
// HTTPS push token: passed as env vars consumed by the entrypoint credential helper.
|
// HTTPS push token: passed as env vars consumed by the entrypoint credential helper.
|
||||||
if let Some(token) = git_token {
|
if let Some(token) = git_token {
|
||||||
@@ -922,6 +907,42 @@ pub async fn handle_new_project(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build the base `docker run` argument list for a project container.
|
||||||
|
///
|
||||||
|
/// Includes `-e HUSKIES_HOST=0.0.0.0` so the server inside the container binds
|
||||||
|
/// to all interfaces, making Docker port forwarding reachable from the host.
|
||||||
|
/// Without this the server defaults to `127.0.0.1` inside the container —
|
||||||
|
/// reachable only from within the container itself, not via `docker -p`.
|
||||||
|
fn project_docker_run_args(
|
||||||
|
container_name: &str,
|
||||||
|
port: u16,
|
||||||
|
ssh_port: u16,
|
||||||
|
pubkey: &str,
|
||||||
|
git_user_name: &str,
|
||||||
|
git_user_email: &str,
|
||||||
|
) -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"run".into(),
|
||||||
|
"-d".into(),
|
||||||
|
"--name".into(),
|
||||||
|
container_name.to_string(),
|
||||||
|
"-p".into(),
|
||||||
|
format!("127.0.0.1:{port}:3001"),
|
||||||
|
"-p".into(),
|
||||||
|
format!("127.0.0.1:{ssh_port}:22"),
|
||||||
|
"-e".into(),
|
||||||
|
"HUSKIES_HOST=0.0.0.0".into(),
|
||||||
|
"-e".into(),
|
||||||
|
"HUSKIES_PORT=3001".into(),
|
||||||
|
"-e".into(),
|
||||||
|
format!("HUSKIES_SSH_PUBKEY={pubkey}"),
|
||||||
|
"-e".into(),
|
||||||
|
format!("GIT_USER_NAME={git_user_name}"),
|
||||||
|
"-e".into(),
|
||||||
|
format!("GIT_USER_EMAIL={git_user_email}"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a failed `docker run` stderr into an actionable chat message.
|
/// Convert a failed `docker run` stderr into an actionable chat message.
|
||||||
///
|
///
|
||||||
/// When Docker cannot find the image locally it prints `Unable to find image`.
|
/// When Docker cannot find the image locally it prints `Unable to find image`.
|
||||||
@@ -1301,6 +1322,32 @@ mod tests {
|
|||||||
|
|
||||||
// ── Missing-image error path ─────────────────────────────────────────────
|
// ── Missing-image error path ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_docker_args_include_huskies_host_env() {
|
||||||
|
let args = project_docker_run_args(
|
||||||
|
"huskies-myapp",
|
||||||
|
3100,
|
||||||
|
2200,
|
||||||
|
"ssh-ed25519 AAAA...",
|
||||||
|
"Test User",
|
||||||
|
"test@example.com",
|
||||||
|
);
|
||||||
|
// Find "-e" followed by "HUSKIES_HOST=0.0.0.0"
|
||||||
|
let pairs: Vec<_> = args.windows(2).collect();
|
||||||
|
assert!(
|
||||||
|
pairs
|
||||||
|
.iter()
|
||||||
|
.any(|w| w[0] == "-e" && w[1] == "HUSKIES_HOST=0.0.0.0"),
|
||||||
|
"expected -e HUSKIES_HOST=0.0.0.0 in docker args, got: {args:?}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
pairs
|
||||||
|
.iter()
|
||||||
|
.any(|w| w[0] == "-e" && w[1] == "HUSKIES_PORT=3001"),
|
||||||
|
"expected -e HUSKIES_PORT=3001 in docker args, got: {args:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interpret_docker_run_error_missing_image_points_at_script() {
|
fn interpret_docker_run_error_missing_image_points_at_script() {
|
||||||
let stderr = "Unable to find image 'huskies-project-rust:latest' locally\n\
|
let stderr = "Unable to find image 'huskies-project-rust:latest' locally\n\
|
||||||
|
|||||||
Reference in New Issue
Block a user