From 263bb495320c96e8a2af30b85ce05e220731a69f Mon Sep 17 00:00:00 2001 From: Jasper Bekkers Date: Mon, 19 Jan 2026 10:08:45 +0100 Subject: [PATCH 1/3] Suppress dead_code warnings in hetzner module Add #[allow(dead_code)] to structs and functions that may be used in the future but are currently unused. Co-Authored-By: Claude Opus 4.5 --- src/hetzner.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/hetzner.rs b/src/hetzner.rs index e30f9e7..c048af7 100644 --- a/src/hetzner.rs +++ b/src/hetzner.rs @@ -43,6 +43,7 @@ pub struct PublicNet { pub ipv4: Ipv4, } +#[allow(dead_code)] #[derive(Debug, Serialize, Deserialize)] pub struct IpAddress { pub ip: String, @@ -89,11 +90,13 @@ struct CreateServerResponse { server: Server, } +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct ErrorResponse { error: ErrorDetail, } +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct ErrorDetail { message: String, @@ -338,7 +341,9 @@ impl HetznerClient { #[derive(Debug, Deserialize)] struct ServerTypesAvailable { available: Vec, + #[allow(dead_code)] available_for_migration: Vec, + #[allow(dead_code)] supported: Vec, } @@ -370,6 +375,7 @@ impl HetznerClient { .collect()) } + #[allow(dead_code)] pub async fn get_server(&self, id: u64) -> Result { let url = format!("{}/servers/{}", HETZNER_API_BASE, id); @@ -448,6 +454,7 @@ final_message: "FFmpeg worker is ready!" } /// Cloud-init config with SSH key access +#[allow(dead_code)] pub fn worker_cloud_init_with_ssh( queue_url: &str, binary_url: &str, From ba83a5b4aff642fc460bda4ef6a2086d83db30fd Mon Sep 17 00:00:00 2001 From: Jasper Bekkers Date: Mon, 19 Jan 2026 10:11:59 +0100 Subject: [PATCH 2/3] Fix missing /api prefix in worker API calls The progress, logs, and status update endpoints were missing the /api prefix in their URLs, causing 404 errors. This is similar to the fix applied earlier for the claim endpoint. Co-Authored-By: Claude Opus 4.5 --- src/jobs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jobs.rs b/src/jobs.rs index a13bf84..12842e2 100644 --- a/src/jobs.rs +++ b/src/jobs.rs @@ -316,7 +316,7 @@ impl RemoteLogger { } let client = reqwest::Client::new(); - let url = format!("{}/jobs/{}/logs", self.queue_url, self.job_id); + let url = format!("{}/api/jobs/{}/logs", self.queue_url, self.job_id); #[derive(Serialize)] struct LogsPayload { @@ -811,7 +811,7 @@ async fn update_job_progress_remote( let client = reqwest::Client::new(); client - .patch(format!("{}/jobs/{}/progress", queue_url, job_id)) + .patch(format!("{}/api/jobs/{}/progress", queue_url, job_id)) .json(&progress) .send() .await @@ -845,7 +845,7 @@ async fn update_job_status_remote( }; client - .patch(format!("{}/jobs/{}", queue_url, job_id)) + .patch(format!("{}/api/jobs/{}", queue_url, job_id)) .json(&update) .send() .await From 30552cb01225dc8a54f30c86a44149f214d9debc Mon Sep 17 00:00:00 2001 From: Jasper Bekkers Date: Mon, 19 Jan 2026 10:14:37 +0100 Subject: [PATCH 3/3] Print root password when provisioning Hetzner server The Hetzner API returns a root_password when creating a server without SSH keys. Now we capture and print this to the terminal so users can SSH into the machine for debugging. Co-Authored-By: Claude Opus 4.5 --- src/hetzner.rs | 29 ++++++++++++++++++++++++----- src/hetzner_cli.rs | 7 +++++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/hetzner.rs b/src/hetzner.rs index c048af7..3d7a89b 100644 --- a/src/hetzner.rs +++ b/src/hetzner.rs @@ -88,6 +88,13 @@ struct CreateServerRequest { #[derive(Debug, Deserialize)] struct CreateServerResponse { server: Server, + root_password: Option, +} + +/// Result of creating a server, includes the server info and root password +pub struct CreateServerResult { + pub server: Server, + pub root_password: Option, } #[allow(dead_code)] @@ -115,7 +122,7 @@ impl HetznerClient { } } - pub async fn create_server(&self, config: &ServerConfig) -> Result { + pub async fn create_server(&self, config: &ServerConfig) -> Result { let url = format!("{}/servers", HETZNER_API_BASE); let labels = if config.labels.is_empty() { @@ -186,7 +193,10 @@ impl HetznerClient { result.server.name, result.server.id, result.server.public_net.ipv4.ip ); - Ok(result.server) + Ok(CreateServerResult { + server: result.server, + root_password: result.root_password, + }) } pub async fn delete_server(&self, id: u64) -> Result<()> { @@ -503,13 +513,19 @@ final_message: "FFmpeg worker is ready!" ) } +/// Result of provisioning a worker +pub struct ProvisionResult { + pub ip: String, + pub root_password: Option, +} + pub async fn provision_worker( hetzner_token: &str, queue_url: &str, binary_url: &str, bg_image_url: &str, name: Option, -) -> Result { +) -> Result { let client = HetznerClient::new(hetzner_token.to_string()); let name = name.unwrap_or_else(|| { @@ -527,6 +543,9 @@ pub async fn provision_worker( ..Default::default() }; - let server = client.create_server(&config).await?; - Ok(server.public_net.ipv4.ip) + let result = client.create_server(&config).await?; + Ok(ProvisionResult { + ip: result.server.public_net.ipv4.ip, + root_password: result.root_password, + }) } diff --git a/src/hetzner_cli.rs b/src/hetzner_cli.rs index 4619882..b914edd 100644 --- a/src/hetzner_cli.rs +++ b/src/hetzner_cli.rs @@ -77,7 +77,7 @@ async fn main() -> Result<()> { let binary_url = format!("{}/assets/worker", base); let bg_image_url = format!("{}/assets/gpc-bg.png", base); - let ip = hetzner::provision_worker( + let result = hetzner::provision_worker( &token, &queue_url, &binary_url, @@ -85,7 +85,10 @@ async fn main() -> Result<()> { name, ) .await?; - println!("Worker provisioned at IP: {}", ip); + println!("Worker provisioned at IP: {}", result.ip); + if let Some(password) = result.root_password { + println!("Root password: {}", password); + } } Commands::ListServers { token } => { let client = hetzner::HetznerClient::new(token);