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
29 changes: 17 additions & 12 deletions rust/Cargo.lock

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

15 changes: 15 additions & 0 deletions rust/agama-manager/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,18 @@ impl SetStorageModel {
impl Message for SetStorageModel {
type Reply = ();
}

#[derive(Clone)]
pub struct SolveStorageModel {
pub model: Value,
}

impl SolveStorageModel {
pub fn new(model: Value) -> Self {
Self { model }
}
}

impl Message for SolveStorageModel {
type Reply = Option<Value>;
}
14 changes: 14 additions & 0 deletions rust/agama-manager/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,20 @@ impl MessageHandler<message::SetStorageModel> for Service {
}
}

#[async_trait]
impl MessageHandler<message::SolveStorageModel> for Service {
/// It solves the storage model.
async fn handle(
&mut self,
message: message::SolveStorageModel,
) -> Result<Option<Value>, Error> {
Ok(self
.storage
.call(storage::message::SolveConfigModel::new(message.model))
.await?)
}
}

// FIXME: write a macro to forward a message.
#[async_trait]
impl MessageHandler<software::message::SetResolvables> for Service {
Expand Down
28 changes: 25 additions & 3 deletions rust/agama-server/src/server/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ use agama_software::Resolvable;
use agama_utils::{
actor::Handler,
api::{
event,
event, query,
question::{Question, QuestionSpec, UpdateQuestion},
Action, Config, IssueMap, Patch, Status, SystemInfo,
},
question,
};
use axum::{
extract::{Path, State},
extract::{Path, Query, State},
response::{IntoResponse, Response},
routing::{get, post, put},
Json, Router,
Expand Down Expand Up @@ -110,11 +110,11 @@ pub async fn server_service(
"/private/storage_model",
get(get_storage_model).put(set_storage_model),
)
.route("/private/solve_storage_model", get(solve_storage_model))
.route("/private/resolvables/:id", put(set_resolvables))
.with_state(state))
}

/// Returns the status of the installation.
#[utoipa::path(
get,
path = "/status",
Expand Down Expand Up @@ -380,6 +380,28 @@ async fn set_storage_model(
Ok(())
}

/// Solves a storage config model.
#[utoipa::path(
get,
path = "/private/solve_storage_model",
context_path = "/api/v2",
params(query::SolveStorageModel),
responses(
(status = 200, description = "Solve the storage model", body = String),
(status = 400, description = "Not possible to solve the storage model")
)
)]
async fn solve_storage_model(
State(state): State<ServerState>,
Query(params): Query<query::SolveStorageModel>,
) -> Result<Json<Option<Value>>, Error> {
let solved_model = state
.manager
.call(message::SolveStorageModel::new(params.model))
.await?;
Ok(Json(solved_model))
}

#[utoipa::path(
put,
path = "/resolvables/:id",
Expand Down
1 change: 1 addition & 0 deletions rust/agama-server/src/web/docs/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ impl ApiDocBuilder for ConfigApiDocBuilder {
.schema_from::<agama_utils::api::question::UpdateQuestion>()
.schema_from::<agama_utils::api::software::RepositoryConfig>()
.schema_from::<agama_utils::api::status::State>()
.schema_from::<agama_utils::api::query::SolveStorageModel>()
.build()
}
}
1 change: 1 addition & 0 deletions rust/agama-utils/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub use action::Action;
pub mod l10n;
pub mod manager;
pub mod network;
pub mod query;
pub mod question;
pub mod software;
pub mod storage;
28 changes: 28 additions & 0 deletions rust/agama-utils/src/api/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use serde::Deserialize;
use serde_json::Value;

#[derive(Deserialize, utoipa::IntoParams, utoipa::ToSchema)]
pub struct SolveStorageModel {
/// Serialized storage model.
pub model: Value,
}
2 changes: 1 addition & 1 deletion rust/share/device.storage.schema.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://github.com/openSUSE/agama/blob/master/rust/share/device.storage.schema.json",
"title": "Storage device",
"title": "Device",
"description": "Schema to describe a device both in 'system' and 'proposal'.",
"type": "object",
"additionalProperties": false,
Expand Down
2 changes: 1 addition & 1 deletion rust/share/system.storage.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"encryptionMethod": {
"title": "Encryption method",
"enum": [
"luks1", "luks2", "pervasiveLuks2", "tmpFde", "protectedSwap", "secureSwap", "randomSwap"
"luks1", "luks2", "pervasiveLuks2", "tpmFde", "protectedSwap", "secureSwap", "randomSwap"
]
},
"volume": {
Expand Down
6 changes: 1 addition & 5 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ import React, { useEffect } from "react";
import { Navigate, Outlet, useLocation } from "react-router";
import { Loading } from "~/components/layout";
import { useProduct, useProductChanges } from "~/queries/software";
import { useProposalChanges } from "~/queries/proposal";
import { useSystemChanges } from "~/queries/system";
import { useIssuesChanges } from "~/queries/issues";
import { useSystemChanges, useProposalChanges, useIssuesChanges } from "~/hooks/api";
import { useInstallerStatus, useInstallerStatusChanges } from "~/queries/status";
import { useDeprecatedChanges } from "~/queries/storage";
import { ROOT, PRODUCT } from "~/routes/paths";
import { InstallationPhase } from "~/types/status";
import { useQueryClient } from "@tanstack/react-query";
Expand All @@ -43,7 +40,6 @@ function App() {
useProductChanges();
useIssuesChanges();
useInstallerStatusChanges();
useDeprecatedChanges();

const location = useLocation();
const { isBusy, phase } = useInstallerStatus({ suspense: true });
Expand Down
114 changes: 114 additions & 0 deletions web/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) [2025] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { get, patch, post, put } from "~/http";
import { apiModel } from "~/api/storage";
import { Config } from "~/api/config";
import { IssuesMap } from "~/api/issue";
import { Proposal } from "~/api/proposal";
import { Question } from "~/api/question";
import { Status } from "~/api/status";
import { System } from "~/api/system";
import {
Action,
L10nSystemConfig,
configureL10n,
activateStorage,
probeStorage,
} from "~/api/action";
import { AxiosResponse } from "axios";
import { Job } from "~/types/job";

type Response = Promise<AxiosResponse>;

const getStatus = (): Promise<Status | null> => get("/api/v2/status");

const getConfig = (): Promise<Config | null> => get("/api/v2/config");

const getExtendedConfig = (): Promise<Config | null> => get("/api/v2/extended_config");

const getSystem = (): Promise<System | null> => get("/api/v2/system");

const getProposal = (): Promise<Proposal | null> => get("/api/v2/proposal");

const getIssues = (): Promise<IssuesMap | null> => get("/api/v2/issues");

const getQuestions = (): Promise<Question[]> => get("/api/v2/questions");

const getStorageModel = (): Promise<apiModel.Config | null> => get("/api/v2/private/storage_model");

const solveStorageModel = (model: apiModel.Config): Promise<apiModel.Config | null> => {
const json = encodeURIComponent(JSON.stringify(model));
return get(`/api/v2/private/solve_storage_model?model=${json}`);
};

const putConfig = (config: Config): Response => put("/api/v2/config", config);

const putStorageModel = (model: apiModel.Config) => put("/api/v2/private/storage_model", model);

const patchConfig = (config: Config) => patch("/api/v2/config", { update: config });

const patchQuestion = (question: Question): Response => {
const {
id,
answer: { action, value },
} = question;
return patch(`/api/v2/questions`, { answer: { id, action, value } });
};

const postAction = (action: Action) => post("/api/v2/action", action);

const configureL10nAction = (config: L10nSystemConfig) => postAction(configureL10n(config));

const activateStorageAction = () => postAction(activateStorage());

const probeStorageAction = () => postAction(probeStorage());

/**
* @todo Adapt jobs to the new API.
*/
const getStorageJobs = (): Promise<Job[]> => get("/api/storage/jobs");

export {
getStatus,
getConfig,
getExtendedConfig,
getSystem,
getProposal,
getIssues,
getQuestions,
getStorageModel,
solveStorageModel,
putConfig,
putStorageModel,
patchConfig,
patchQuestion,
configureL10nAction,
activateStorageAction,
probeStorageAction,
getStorageJobs,
};

export type { Response, System, Config, Proposal };
export * as system from "~/api/system";
export * as config from "~/api/config";
export * as proposal from "~/api/proposal";
Loading
Loading