Skip to content

Commit

Permalink
Expose the manager service through the HTTP/JSON API (#1089)
Browse files Browse the repository at this point in the history
Trello:
https://trello.com/c/Ff8n3NVZ/3563-5-expose-the-manager-api-over-http

This PR exposes most of the Manager API[^1] through the interface. It
includes:

* `api/manager/probe`: starts the probing.
* `api/manager/install`: starts the installation.
* `api/manager/finish`: executes the post-install tasks.
* `api/manager/status`: expose the manager status`

```rust
{
  "phase": "Config",
  "busy": [
    "org.opensuse.Agama.Storage1"
  ]
}
```

Additionally, it emits two events: `BusyServicesChanged` and
`InstallationPhaseChanged`.

[^1]: The part of the D-Bus API used by the web UI.

## To do

- [x] Before proceeding, we need to merge #1080.
- [ ] ~~Add a mechanism to get the logs~~ (postponed).
- [x] Add a `CanInstall` boolean to the manager status.
  • Loading branch information
imobachgs authored Mar 13, 2024
2 parents fbb5049 + 108c741 commit 3a6ce33
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 11 deletions.
36 changes: 36 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions rust/agama-lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub enum ServiceError {
UnknownPatterns(Vec<String>),
#[error("Could not perform action '{0}'")]
UnsuccessfulAction(String),
#[error("Unknown installation phase: '{0}")]
UnknownInstallationPhase(u32),
}

#[derive(Error, Debug)]
Expand Down
55 changes: 52 additions & 3 deletions rust/agama-lib/src/manager.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,94 @@
//! This module implements the web API for the manager module.
use crate::error::ServiceError;
use crate::proxies::ServiceStatusProxy;
use crate::{
progress::Progress,
proxies::{ManagerProxy, ProgressProxy},
proxies::{Manager1Proxy, ProgressProxy},
};
use serde::Serialize;
use tokio_stream::StreamExt;
use zbus::Connection;

/// D-Bus client for the manager service
#[derive(Clone)]
pub struct ManagerClient<'a> {
manager_proxy: ManagerProxy<'a>,
manager_proxy: Manager1Proxy<'a>,
progress_proxy: ProgressProxy<'a>,
status_proxy: ServiceStatusProxy<'a>,
}

/// Represents the installation phase.
#[derive(Clone, Copy, Debug, PartialEq, Serialize, utoipa::ToSchema)]
pub enum InstallationPhase {
/// Start up phase.
Startup,
/// Configuration phase.
Config,
/// Installation phase.
Install,
}

impl TryFrom<u32> for InstallationPhase {
type Error = ServiceError;

fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Startup),
1 => Ok(Self::Config),
2 => Ok(Self::Install),
_ => Err(ServiceError::UnknownInstallationPhase(value)),
}
}
}

impl<'a> ManagerClient<'a> {
pub async fn new(connection: Connection) -> zbus::Result<ManagerClient<'a>> {
Ok(Self {
manager_proxy: ManagerProxy::new(&connection).await?,
manager_proxy: Manager1Proxy::new(&connection).await?,
progress_proxy: ProgressProxy::new(&connection).await?,
status_proxy: ServiceStatusProxy::new(&connection).await?,
})
}

/// Returns the list of busy services.
pub async fn busy_services(&self) -> Result<Vec<String>, ServiceError> {
Ok(self.manager_proxy.busy_services().await?)
}

/// Returns the current installation phase.
pub async fn current_installation_phase(&self) -> Result<InstallationPhase, ServiceError> {
let phase = self.manager_proxy.current_installation_phase().await?;
Ok(phase.try_into()?)
}

/// Starts the probing process.
pub async fn probe(&self) -> Result<(), ServiceError> {
self.wait().await?;
Ok(self.manager_proxy.probe().await?)
}

/// Starts the installation.
pub async fn install(&self) -> Result<(), ServiceError> {
Ok(self.manager_proxy.commit().await?)
}

/// Executes the after installation tasks.
pub async fn finish(&self) -> Result<(), ServiceError> {
Ok(self.manager_proxy.finish().await?)
}

/// Determines whether it is possible to start the installation.
pub async fn can_install(&self) -> Result<bool, ServiceError> {
Ok(self.manager_proxy.can_install().await?)
}

/// Determines whether the installer is running on Iguana.
pub async fn use_iguana(&self) -> Result<bool, ServiceError> {
Ok(self.manager_proxy.iguana_backend().await?)
}

/// Returns the current progress.
pub async fn progress(&self) -> zbus::Result<Progress> {
Progress::from_proxy(&self.progress_proxy).await
}
Expand Down
11 changes: 9 additions & 2 deletions rust/agama-lib/src/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,19 @@ trait ServiceStatus {
default_service = "org.opensuse.Agama.Manager1",
default_path = "/org/opensuse/Agama/Manager1"
)]
trait Manager {
trait Manager1 {
/// CanInstall method
fn can_install(&self) -> zbus::Result<bool>;

/// CollectLogs method
fn collect_logs(&self, user: &str) -> zbus::Result<String>;
fn collect_logs(&self) -> zbus::Result<String>;

/// Commit method
fn commit(&self) -> zbus::Result<()>;

/// Finish method
fn finish(&self) -> zbus::Result<()>;

/// Probe method
fn probe(&self) -> zbus::Result<()>;

Expand All @@ -68,6 +71,10 @@ trait Manager {
#[dbus_proxy(property)]
fn current_installation_phase(&self) -> zbus::Result<u32>;

/// IguanaBackend property
#[dbus_proxy(property)]
fn iguana_backend(&self) -> zbus::Result<bool>;

/// InstallationPhases property
#[dbus_proxy(property)]
fn installation_phases(
Expand Down
1 change: 1 addition & 0 deletions rust/agama-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ path = "src/agama-web-server.rs"

[dev-dependencies]
http-body-util = "0.1.0"
tokio-test = "0.4.3"
2 changes: 1 addition & 1 deletion rust/agama-server/src/l10n/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn locales(State(state): State<LocaleState>) -> Json<Vec<LocaleEntry>> {
Json(locales)
}

#[derive(Clone, Default, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, utoipa::ToSchema)]
pub struct LocaleConfig {
/// Locales to install in the target system
locales: Option<Vec<String>>,
Expand Down
1 change: 1 addition & 0 deletions rust/agama-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod error;
pub mod l10n;
pub mod manager;
pub mod network;
pub mod questions;
pub mod software;
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-server/src/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod web;
pub use web::manager_service;
Loading

0 comments on commit 3a6ce33

Please sign in to comment.