Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 85 additions & 79 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tonic = "0.13.0"
prost = "0.13.5"
prost-types = "0.13.5"
anyhow = "1.0.100"
nix = { version = "0.29.0", features = ["mount", "user", "reboot", "feature", "net", "aio", "signal", "process", "fs", "hostname"] }
nix = { version = "0.30.1", features = ["mount", "user", "reboot", "feature", "net", "aio", "signal", "process", "fs", "hostname"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.132"
uuid = { version = "1.18.1", features = ["v4"] }
Expand All @@ -30,8 +30,8 @@ log = "0.4.28"
env_logger = "0.11"
openssl = { version = "0.10.72", features = ["vendored"] }
oci-distribution = "0.11.0"
tempfile = "3.22.0"
tower = { version = "0.4", features = ["full"] }
tempfile = "3.23.0"
tower = { version = "0.5.2", features = ["full"] }
sha2 = "0.10"
hex = "0.4"
digest = "0.10"
Expand All @@ -45,5 +45,9 @@ dhcproto = "0.13.0"
socket2 = "0.6.0"
futures = "0.3.31"
chrono = "0.4.42"
hyper-util = { version = "0.1.14", features = ["tokio"] }
thiserror = "2.0.16"
hyper = "1.7.0"
hyper-util = { version = "0.1.17", features = ["full"] }
termcolor = "1.1"
once_cell = "1.19"
feos-proto = { path = "feos/proto" }
12 changes: 4 additions & 8 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ edition.workspace = true
description = "A gRPC CLI for the FeOS control plane"

[dependencies]
feos-proto = { workspace = true }
sha2 = { workspace = true }
hex = { workspace = true }
digest = { workspace = true }

# Workspace dependencies
feos-proto = { workspace = true }
tokio = { workspace = true }
tonic = { workspace = true }
anyhow = { workspace = true }
Expand All @@ -20,8 +16,8 @@ hyper-util = { workspace = true }
prost = { workspace = true }
tower = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
env_logger = { workspace = true }
chrono = { workspace = true }

# CLI specific dependencies
env_logger = { workspace = true }
crossterm = "0.29"
chrono = { workspace = true }
crossterm = "0.29"
7 changes: 3 additions & 4 deletions feos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dhcproto = { workspace = true }
socket2 = { workspace = true }
futures = { workspace = true }
chrono = { workspace = true }
termcolor = "1.1"
termcolor = { workspace = true }

[dev-dependencies]
feos-utils = { path = "utils" }
Expand All @@ -54,7 +54,6 @@ env_logger = { workspace = true }
tokio-stream = { workspace = true }
prost = { workspace = true }
hyper-util = { workspace = true }
once_cell = "1.19"
regex = "1.10"
once_cell = { workspace = true }
tower = { workspace = true }
tempfile = "3.10.1"
tempfile = { workspace = true }
17 changes: 9 additions & 8 deletions feos/services/host-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ tempfile = { workspace = true }
sha2 = { workspace = true }
hex = { workspace = true }
digest = { workspace = true }
hyper = "1.4.0"
hyper-util = { version = "0.1.3", features = ["full"] }
hyper-rustls = "0.27.2"
http-body-util = "0.1.2"
rustls-pki-types = "1.0"

# Workspace dependencies
tokio = { workspace = true }
tokio-stream = { workspace = true }
tonic = { workspace = true }
anyhow = { workspace = true }
nix = { workspace = true , features = ["hostname", "reboot"] }
log = { workspace = true }
prost = { workspace = true }
prost-types = { workspace = true }
prost-types = { workspace = true }
thiserror = { workspace = true }
hyper = {workspace = true}
hyper-util = { workspace = true }


hyper-rustls = "0.27.2"
http-body-util = "0.1.2"
rustls-pki-types = "1.0"
153 changes: 41 additions & 112 deletions feos/services/host-service/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ impl HostApiHandler {
}
}

async fn dispatch_and_wait<T, E>(
dispatcher: &mpsc::Sender<Command>,
command_constructor: impl FnOnce(oneshot::Sender<Result<T, E>>) -> Command,
) -> Result<Response<T>, Status>
where
E: Into<Status>,
{
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = command_constructor(resp_tx);

dispatcher
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(e)) => Err(e.into()),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
}

#[tonic::async_trait]
impl HostService for HostApiHandler {
type StreamKernelLogsStream =
Expand All @@ -36,146 +60,64 @@ impl HostService for HostApiHandler {
_request: Request<HostnameRequest>,
) -> Result<Response<HostnameResponse>, Status> {
info!("HostApi: Received Hostname request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::GetHostname(resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, Command::GetHostname).await
}

async fn get_memory(
&self,
_request: Request<MemoryRequest>,
) -> Result<Response<MemoryResponse>, Status> {
info!("HostApi: Received GetMemory request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::GetMemory(resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, Command::GetMemory).await
}

async fn get_cpu_info(
&self,
_request: Request<GetCpuInfoRequest>,
) -> Result<Response<GetCpuInfoResponse>, Status> {
info!("HostApi: Received GetCPUInfo request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::GetCPUInfo(resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, Command::GetCPUInfo).await
}

async fn get_network_info(
&self,
_request: Request<GetNetworkInfoRequest>,
) -> Result<Response<GetNetworkInfoResponse>, Status> {
info!("HostApi: Received GetNetworkInfo request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::GetNetworkInfo(resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, Command::GetNetworkInfo).await
}

async fn shutdown(
&self,
request: Request<ShutdownRequest>,
) -> Result<Response<ShutdownResponse>, Status> {
info!("HostApi: Received Shutdown request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::Shutdown(request.into_inner(), resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, |resp_tx| {
Command::Shutdown(request.into_inner(), resp_tx)
})
.await
}

async fn reboot(
&self,
request: Request<RebootRequest>,
) -> Result<Response<RebootResponse>, Status> {
info!("HostApi: Received Reboot request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::Reboot(request.into_inner(), resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, |resp_tx| {
Command::Reboot(request.into_inner(), resp_tx)
})
.await
}

async fn upgrade_feos_binary(
&self,
request: Request<UpgradeFeosBinaryRequest>,
) -> Result<Response<UpgradeFeosBinaryResponse>, Status> {
info!("HostApi: Received UpgradeFeosBinary request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::UpgradeFeosBinary(request.into_inner(), resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, |resp_tx| {
Command::UpgradeFeosBinary(request.into_inner(), resp_tx)
})
.await
}

async fn stream_kernel_logs(
Expand Down Expand Up @@ -213,19 +155,6 @@ impl HostService for HostApiHandler {
_request: Request<GetVersionInfoRequest>,
) -> Result<Response<GetVersionInfoResponse>, Status> {
info!("HostApi: Received GetVersionInfo request.");
let (resp_tx, resp_rx) = oneshot::channel();
let cmd = Command::GetVersionInfo(resp_tx);
self.dispatcher_tx
.send(cmd)
.await
.map_err(|e| Status::internal(format!("Failed to send command to dispatcher: {e}")))?;

match resp_rx.await {
Ok(Ok(result)) => Ok(Response::new(result)),
Ok(Err(status)) => Err(status),
Err(_) => Err(Status::internal(
"Dispatcher task dropped response channel.",
)),
}
dispatch_and_wait(&self.dispatcher_tx, Command::GetVersionInfo).await
}
}
38 changes: 38 additions & 0 deletions feos/services/host-service/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0

use tonic::Status;

#[derive(Debug, thiserror::Error)]
pub enum HostError {
#[error("Failed to get system hostname")]
Hostname(#[from] nix::Error),

#[error("Failed to read system info from {path}")]
SystemInfoRead {
#[source]
source: std::io::Error,
path: String,
},

#[error("Host power operation failed")]
PowerOperation(#[source] nix::Error),

#[error("Failed to create log reader: {0}")]
LogReader(String),
}

impl From<HostError> for Status {
fn from(err: HostError) -> Self {
log::error!("HostServiceError: {err}");
match err {
HostError::SystemInfoRead { path, .. } => {
Status::internal(format!("Failed to read system info from {path}"))
}
HostError::Hostname(_) | HostError::PowerOperation(_) => {
Status::internal("An internal host error occurred")
}
HostError::LogReader(msg) => Status::internal(msg),
}
}
}
Loading
Loading