Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose the software service through the HTTP/JSON API #1069

Merged
merged 24 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e75dcf8
rust: add a software service
imobachgs Feb 29, 2024
d424b68
rust: add support to select a product via HTTP
imobachgs Feb 29, 2024
57cac7f
rust: emit product changes
imobachgs Feb 29, 2024
fc3e12a
rust: move software config to a separate resource
imobachgs Mar 1, 2024
2b5be30
rust: share the SoftwareProductProxy
imobachgs Mar 1, 2024
5edea12
rust: use the software client in the web API
imobachgs Mar 1, 2024
9c65f85
rust: add patterns support to the HTTP API
imobachgs Mar 1, 2024
0cbb436
rust: emit patterns selection changes
imobachgs Mar 1, 2024
35ffb0e
rust: emit stream events in a single place
imobachgs Mar 2, 2024
6340803
doc: update the OpenAPI documentation
imobachgs Mar 4, 2024
0e0b0e3
rust: refactor the CLI arguments handling
imobachgs Mar 4, 2024
6e9d476
rust: fix service tests
imobachgs Mar 4, 2024
602a6f8
rust: remove unneeded lifetime annotations
imobachgs Mar 4, 2024
4417e43
software web functions return an result
imobachgs Mar 4, 2024
4679589
rust: add an endpoint /software/proposal
imobachgs Mar 4, 2024
4896fb2
rust: add an endpoint to start the software probing
imobachgs Mar 4, 2024
6d6ee2d
rust: PatternsChanged includes the list of patterns
imobachgs Mar 4, 2024
ab55a05
rust: document used_disk_space
imobachgs Mar 4, 2024
c1b2853
rust: merge PatternStatus and SelectedBy
imobachgs Mar 5, 2024
0a57873
rust: handle errors when creating the service router
imobachgs Mar 5, 2024
9551eae
rust: fix service tests
imobachgs Mar 5, 2024
6a6cf06
rust: add missing elements to the OpenAPI documentation
imobachgs Mar 5, 2024
7cbe2b7
rust: improve SoftwareError description
imobachgs Mar 5, 2024
4c0cb3b
rust: handle invalid pattern selection reasons
imobachgs Mar 5, 2024
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
1 change: 1 addition & 0 deletions rust/Cargo.lock

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

1 change: 1 addition & 0 deletions rust/agama-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ thiserror = "1.0.39"
tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] }
tokio-stream = "0.1.14"
url = "2.5.0"
utoipa = "4.2.0"
zbus = { version = "3", default-features = false, features = ["tokio"] }
2 changes: 1 addition & 1 deletion rust/agama-lib/src/product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ mod proxies;
mod settings;
mod store;

pub use client::ProductClient;
pub use client::{Product, ProductClient};
pub use settings::ProductSettings;
pub use store::ProductStore;
3 changes: 2 additions & 1 deletion rust/agama-lib/src/product/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use zbus::Connection;
use super::proxies::RegistrationProxy;

/// Represents a software product
#[derive(Debug, Serialize)]
#[derive(Default, Debug, Serialize, utoipa::ToSchema)]
pub struct Product {
/// Product ID (eg., "ALP", "Tumbleweed", etc.)
pub id: String,
Expand All @@ -19,6 +19,7 @@ pub struct Product {
}

/// D-Bus client for the software service
#[derive(Clone)]
pub struct ProductClient<'a> {
product_proxy: SoftwareProductProxy<'a>,
registration_proxy: RegistrationProxy<'a>,
Expand Down
2 changes: 1 addition & 1 deletion rust/agama-lib/src/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ pub mod proxies;
mod settings;
mod store;

pub use client::SoftwareClient;
pub use client::{Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy};
pub use settings::SoftwareSettings;
pub use store::SoftwareStore;
70 changes: 66 additions & 4 deletions rust/agama-lib/src/software/client.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::proxies::Software1Proxy;
use crate::error::ServiceError;
use serde::Serialize;
use std::collections::HashMap;
use zbus::Connection;

/// Represents a software product
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct Pattern {
/// Pattern ID (eg., "aaa_base", "gnome")
pub id: String,
Expand All @@ -20,7 +21,35 @@ pub struct Pattern {
pub order: String,
}

/// Represents the reason why a pattern is selected.
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub enum SelectedBy {
/// The pattern was selected by the user.
User = 0,
/// The pattern was selected automatically.
Auto = 1,
/// The pattern has not be selected.
None = 2,
}

#[derive(Debug, thiserror::Error)]
#[error("Unknown selected by value: '{0}'")]
pub struct UnknownSelectedBy(u8);

impl TryFrom<u8> for SelectedBy {
type Error = UnknownSelectedBy;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::User),
1 => Ok(Self::Auto),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now, I am confused, why is not there 2 => Ok(Self::None)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is not expected to get a 2 from the API (it does not exist).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, ok.

_ => Err(UnknownSelectedBy(value)),
}
}
}

/// D-Bus client for the software service
#[derive(Clone)]
pub struct SoftwareClient<'a> {
software_proxy: Software1Proxy<'a>,
}
Expand Down Expand Up @@ -55,14 +84,35 @@ impl<'a> SoftwareClient<'a> {

/// Returns the ids of patterns selected by user
pub async fn user_selected_patterns(&self) -> Result<Vec<String>, ServiceError> {
const USER_SELECTED: u8 = 0;
let patterns: Vec<String> = self
.software_proxy
.selected_patterns()
.await?
.into_iter()
.filter(|(_id, reason)| *reason == USER_SELECTED)
.map(|(id, _reason)| id)
.filter_map(|(id, reason)| match SelectedBy::try_from(reason) {
Ok(reason) if reason == SelectedBy::User => Some(id),
Ok(_reason) => None,
Err(e) => {
log::warn!("Ignoring pattern {}. Error: {}", &id, e);
None
}
})
.collect();
Ok(patterns)
}

/// Returns the selected pattern and the reason each one selected.
pub async fn selected_patterns(&self) -> Result<HashMap<String, SelectedBy>, ServiceError> {
let patterns = self.software_proxy.selected_patterns().await?;
let patterns = patterns
.into_iter()
.filter_map(|(id, reason)| match SelectedBy::try_from(reason) {
Ok(reason) => Some((id, reason)),
Err(e) => {
log::warn!("Ignoring pattern {}. Error: {}", &id, e);
None
}
})
.collect();
Ok(patterns)
}
Expand All @@ -80,4 +130,16 @@ impl<'a> SoftwareClient<'a> {
Ok(())
}
}

/// Returns the required space for installing the selected patterns.
///
/// It returns a formatted string including the size and the unit.
pub async fn used_disk_space(&self) -> Result<String, ServiceError> {
Ok(self.software_proxy.used_disk_space().await?)
}

/// Starts the process to read the repositories data.
pub async fn probe(&self) -> Result<(), ServiceError> {
Ok(self.software_proxy.probe().await?)
}
}
37 changes: 24 additions & 13 deletions rust/agama-server/src/agama-web-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@ use agama_dbus_server::{
l10n::helpers,
web::{self, run_monitor},
};
use clap::{Parser, Subcommand};
use agama_lib::connection_to;
use clap::{Args, Parser, Subcommand};
use tokio::sync::broadcast::channel;
use tracing_subscriber::prelude::*;
use utoipa::OpenApi;

#[derive(Subcommand, Debug)]
enum Commands {
/// Start the API server.
Serve {
// Address to listen on (":::3000" listens for both IPv6 and IPv4
// connections unless manually disabled in /proc/sys/net/ipv6/bindv6only)
#[arg(long, default_value = ":::3000")]
address: String,
},
Serve(ServeArgs),
/// Display the API documentation in OpenAPI format.
Openapi,
}

#[derive(Debug, Args)]
pub struct ServeArgs {
// Address to listen on (":::3000" listens for both IPv6 and IPv4
// connections unless manually disabled in /proc/sys/net/ipv6/bindv6only)
#[arg(long, default_value = ":::3000")]
address: String,
// Agama D-Bus address
#[arg(long, default_value = "unix:path=/run/agama/bus")]
dbus_address: String,
}

#[derive(Parser, Debug)]
#[command(
version,
Expand All @@ -33,22 +40,26 @@ struct Cli {
}

/// Start serving the API.
async fn serve_command(address: &str) -> anyhow::Result<()> {
///
/// `args`: command-line arguments.
async fn serve_command(args: ServeArgs) -> anyhow::Result<()> {
let journald = tracing_journald::layer().expect("could not connect to journald");
tracing_subscriber::registry().with(journald).init();

let listener = tokio::net::TcpListener::bind(address)
let listener = tokio::net::TcpListener::bind(&args.address)
.await
.unwrap_or_else(|_| panic!("could not listen on {}", address));
.unwrap_or_else(|_| panic!("could not listen on {}", &args.address));

let (tx, _) = channel(16);
run_monitor(tx.clone()).await?;

let config = web::ServiceConfig::load().unwrap();
let service = web::service(config, tx);
let config = web::ServiceConfig::load()?;
let dbus = connection_to(&args.dbus_address).await?;
let service = web::service(config, tx, dbus).await?;
axum::serve(listener, service)
.await
.expect("could not mount app on listener");

Ok(())
}

Expand All @@ -60,7 +71,7 @@ fn openapi_command() -> anyhow::Result<()> {

async fn run_command(cli: Cli) -> anyhow::Result<()> {
match cli.command {
Commands::Serve { address } => serve_command(&address).await,
Commands::Serve(args) => serve_command(args).await,
Commands::Openapi => openapi_command(),
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/agama-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub mod error;
pub mod l10n;
pub mod network;
pub mod questions;
pub mod software;
pub mod web;
pub use web::service;
2 changes: 2 additions & 0 deletions rust/agama-server/src/software.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod web;
pub use web::{software_service, software_stream};
Loading
Loading