Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into import_cli
Browse files Browse the repository at this point in the history
  • Loading branch information
jreidinger committed May 29, 2024
2 parents 7dd2f3b + a45601d commit b66daa6
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 162 deletions.
65 changes: 33 additions & 32 deletions rust/agama-server/src/agama-web-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,25 @@ const TOKEN_FILE: &str = "/run/agama/token";

#[derive(Subcommand, Debug)]
enum Commands {
/// Start the API server.
/// Starts the API server.
///
/// This command starts the server in the given ports. The secondary port, if enabled, uses SSL.
/// If no certificate is specified, agama-web-server generates a self-signed one.
Serve(ServeArgs),
/// Display the API documentation in OpenAPI format.
/// Generates the API documentation in OpenAPI format.
Openapi,
}

/// Manage Agama's HTTP/JSON API.
///
/// Agama's public interface is composed by an HTTP/JSON API and a WebSocket. Using this API is
/// possible to inspect or change the configuration, start the installation process and monitor
/// changes and progress. This program, agama-web-server, implements such an API.
///
/// To start the API, use the "serve" command. If you want to get an OpenAPI representation, just go
/// for the "doc" command.
#[derive(Parser, Debug)]
#[command(
version,
about = "Starts the Agama web-based API.",
long_about = None)]
#[command(max_term_width = 100)]
struct Cli {
#[command(subcommand)]
pub command: Commands,
Expand All @@ -64,36 +72,29 @@ fn find_web_ui_dir() -> PathBuf {

#[derive(Args, Debug)]
struct ServeArgs {
// Address/port to listen on (":::80" listens for both IPv6 and IPv4
// connections unless manually disabled in /proc/sys/net/ipv6/bindv6only)
#[arg(long, default_value = ":::80", help = "Primary address to listen on")]
// Address/port to listen on. ":::80" listens for both IPv6 and IPv4
// connections unless manually disabled in /proc/sys/net/ipv6/bindv6only.
/// Primary port to listen on
#[arg(long, default_value = ":::80")]
address: String,
#[arg(
long,
default_value = None,
help = "Optional secondary address to listen on"
)]

/// Optional secondary address to listen on
#[arg(long, default_value = None)]
address2: Option<String>,
#[arg(
long,
default_value = None,
help = "Path to the SSL private key file in PEM format"
)]

/// Path to the SSL private key file in PEM format
#[arg(long, default_value = None)]
key: Option<String>,
#[arg(
long,
default_value = None,
help = "Path to the SSL certificate file in PEM format"
)]

/// Path to the SSL certificate file in PEM format
#[arg(long, default_value = None)]
cert: Option<String>,
// Agama D-Bus address
#[arg(
long,
default_value = "unix:path=/run/agama/bus",
help = "The D-Bus address for connecting to the Agama service"
)]

/// The D-Bus address for connecting to the Agama service
#[arg(long, default_value = "unix:path=/run/agama/bus")]
dbus_address: String,
// Directory containing the web UI code.

// Directory containing the web UI code
#[arg(long)]
web_ui_dir: Option<PathBuf>,
}
Expand Down Expand Up @@ -292,6 +293,7 @@ async fn start_server(address: String, service: Router, ssl_acceptor: SslAccepto
/// Start serving the API.
/// `options`: command-line arguments.
async fn serve_command(args: ServeArgs) -> anyhow::Result<()> {
_ = helpers::init_locale();
init_logging().context("Could not initialize the logger")?;

let (tx, _) = channel(16);
Expand Down Expand Up @@ -376,7 +378,6 @@ impl Termination for CliResult {
#[tokio::main]
async fn main() -> CliResult {
let cli = Cli::parse();
_ = helpers::init_locale();

if let Err(error) = run_command(cli).await {
eprintln!("{:?}", error);
Expand Down
31 changes: 26 additions & 5 deletions rust/agama-server/src/manager/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{

#[derive(Clone)]
pub struct ManagerState<'a> {
dbus: zbus::Connection,
manager: ManagerClient<'a>,
}

Expand Down Expand Up @@ -86,8 +87,8 @@ pub async fn manager_service(dbus: zbus::Connection) -> Result<Router, ServiceEr

let status_router = service_status_router(&dbus, DBUS_SERVICE, DBUS_PATH).await?;
let progress_router = progress_router(&dbus, DBUS_SERVICE, DBUS_PATH).await?;
let manager = ManagerClient::new(dbus).await?;
let state = ManagerState { manager };
let manager = ManagerClient::new(dbus.clone()).await?;
let state = ManagerState { manager, dbus };
Ok(Router::new()
.route("/probe", post(probe_action))
.route("/install", post(install_action))
Expand All @@ -100,16 +101,36 @@ pub async fn manager_service(dbus: zbus::Connection) -> Result<Router, ServiceEr
}

/// Starts the probing process.
// The Probe D-Bus method is blocking and will not return until the probing is finished. To avoid a
// long-lived HTTP connection, this method returns immediately (with a 200) and runs the request on
// a separate task.
#[utoipa::path(
get,
path = "/probe",
context_path = "/api/manager",
responses(
(status = 200, description = "The probing process was started.")
(
status = 200,
description = "The probing was requested but there is no way to know whether it succeeded."
)
)
)]
async fn probe_action(State(state): State<ManagerState<'_>>) -> Result<(), Error> {
state.manager.probe().await?;
async fn probe_action<'a>(State(state): State<ManagerState<'a>>) -> Result<(), Error> {
let dbus = state.dbus.clone();
tokio::spawn(async move {
let result = dbus
.call_method(
Some("org.opensuse.Agama.Manager1"),
"/org/opensuse/Agama/Manager1",
Some("org.opensuse.Agama.Manager1"),
"Probe",
&(),
)
.await;
if let Err(error) = result {
tracing::error!("Could not start probing: {:?}", error);
}
});
Ok(())
}

Expand Down
19 changes: 17 additions & 2 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
-------------------------------------------------------------------
Wed May 29 07:15:37 UTC 2024 - Josef Reidinger <[email protected]>
Wed May 29 12:15:37 UTC 2024 - Josef Reidinger <[email protected]>

- CLI: Add new command "agama profile import" that does the whole
autoinstallation processing and loads the configuration.
autoinstallation processing and loads the configuration
(gh#openSUSE/agama#1270).

-------------------------------------------------------------------
Wed May 29 11:16:11 UTC 2024 - Imobach Gonzalez Sosa <[email protected]>

- Improve command-line interface help (gh#openSUSE/agama#1269 and
(gh#openSUSE/agama#1273).
- agama-web-server connects to D-Bus only when needed
(gh#openSUSE/agama#1273).

-------------------------------------------------------------------
Wed May 29 10:40:21 UTC 2024 - Imobach Gonzalez Sosa <[email protected]>

- The HTTP request to perform a probing is not blocking anymore
(gh#openSUSE/agama#1272).

-------------------------------------------------------------------
Mon May 27 14:11:55 UTC 2024 - Imobach Gonzalez Sosa <[email protected]>
Expand Down
12 changes: 4 additions & 8 deletions web/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,10 @@ import { useProduct } from "./context/product";
import { INSTALL, STARTUP } from "~/client/phase";
import { BUSY } from "~/client/status";

import { DBusError, If, Installation } from "~/components/core";
import { ServerError, If, Installation } from "~/components/core";
import { Loading } from "./components/layout";
import { useInstallerL10n } from "./context/installerL10n";

// D-Bus connection attempts before displaying an error.
const ATTEMPTS = 3;

/**
* Main application component.
*
Expand All @@ -43,7 +40,7 @@ const ATTEMPTS = 3;
*/
function App() {
const client = useInstallerClient();
const { attempt } = useInstallerClientStatus();
const { error } = useInstallerClientStatus();
const { products } = useProduct();
const { language } = useInstallerL10n();
const [status, setStatus] = useState(undefined);
Expand Down Expand Up @@ -75,9 +72,8 @@ function App() {
}, [client, setPhase, setStatus]);

const Content = () => {
if (!client || !products) {
return (attempt > ATTEMPTS) ? <DBusError /> : <Loading />;
}
if (error) return <ServerError />;
if (!products) return <Loading />;

if ((phase === STARTUP && status === BUSY) || phase === undefined || status === undefined) {
return <Loading />;
Expand Down
Loading

0 comments on commit b66daa6

Please sign in to comment.