-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
agama config load/store for "storage" uses the HTTP API (#1600)
## Problem - https://trello.com/c/hvPtBtMD To recap: > When moving to the new HTTP-based architecture, we took some shortcuts. One of them was not using HTTP clients in the command-line interface. ## Solution - Add `StorageHTTPClient` - This 🛑 ends D-Bus 🛑 usage from `SettingsStore` ✔️ ## Testing - Added a new unit test - Tested manually: note that it is easy to pass invalid Storage config in this way which will confuse the UI to the point of not rendering the storage parts at all. ## Screenshots No
- Loading branch information
Showing
8 changed files
with
178 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
//! Implements a client to access Agama's storage service. | ||
use crate::base_http_client::BaseHTTPClient; | ||
use crate::storage::StorageSettings; | ||
use crate::ServiceError; | ||
|
||
pub struct StorageHTTPClient { | ||
client: BaseHTTPClient, | ||
} | ||
|
||
impl StorageHTTPClient { | ||
pub fn new() -> Result<Self, ServiceError> { | ||
Ok(Self { | ||
client: BaseHTTPClient::new()?, | ||
}) | ||
} | ||
|
||
pub fn new_with_base(base: BaseHTTPClient) -> Self { | ||
Self { client: base } | ||
} | ||
|
||
pub async fn get_config(&self) -> Result<StorageSettings, ServiceError> { | ||
self.client.get("/storage/config").await | ||
} | ||
|
||
pub async fn set_config(&self, config: &StorageSettings) -> Result<(), ServiceError> { | ||
self.client.put_void("/storage/config", config).await | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,102 @@ | ||
//! Implements the store for the storage settings. | ||
use super::{StorageClient, StorageSettings}; | ||
use super::StorageSettings; | ||
use crate::error::ServiceError; | ||
use zbus::Connection; | ||
use crate::storage::http_client::StorageHTTPClient; | ||
|
||
/// Loads and stores the storage settings from/to the D-Bus service. | ||
pub struct StorageStore<'a> { | ||
storage_client: StorageClient<'a>, | ||
/// Loads and stores the storage settings from/to the HTTP service. | ||
pub struct StorageStore { | ||
storage_client: StorageHTTPClient, | ||
} | ||
|
||
impl<'a> StorageStore<'a> { | ||
pub async fn new(connection: Connection) -> Result<StorageStore<'a>, ServiceError> { | ||
impl StorageStore { | ||
pub fn new() -> Result<StorageStore, ServiceError> { | ||
Ok(Self { | ||
storage_client: StorageClient::new(connection).await?, | ||
storage_client: StorageHTTPClient::new()?, | ||
}) | ||
} | ||
|
||
pub async fn load(&self) -> Result<StorageSettings, ServiceError> { | ||
Ok(self.storage_client.get_config().await?) | ||
} | ||
|
||
pub async fn store(&self, settings: StorageSettings) -> Result<(), ServiceError> { | ||
pub async fn store(&self, settings: &StorageSettings) -> Result<(), ServiceError> { | ||
self.storage_client.set_config(settings).await?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
use crate::base_http_client::BaseHTTPClient; | ||
use httpmock::prelude::*; | ||
use std::error::Error; | ||
use tokio::test; // without this, "error: async functions cannot be used for tests" | ||
|
||
fn storage_store(mock_server_url: String) -> StorageStore { | ||
let mut bhc = BaseHTTPClient::default(); | ||
bhc.base_url = mock_server_url; | ||
let client = StorageHTTPClient::new_with_base(bhc); | ||
StorageStore { | ||
storage_client: client, | ||
} | ||
} | ||
|
||
#[test] | ||
async fn test_getting_storage() -> Result<(), Box<dyn Error>> { | ||
let server = MockServer::start(); | ||
let storage_mock = server.mock(|when, then| { | ||
when.method(GET).path("/api/storage/config"); | ||
then.status(200) | ||
.header("content-type", "application/json") | ||
.body( | ||
r#"{ | ||
"storage": { "some": "stuff" } | ||
}"#, | ||
); | ||
}); | ||
let url = server.url("/api"); | ||
|
||
let store = storage_store(url); | ||
let settings = store.load().await?; | ||
|
||
// main assertion | ||
assert_eq!(settings.storage.unwrap().get(), r#"{ "some": "stuff" }"#); | ||
assert!(settings.storage_autoyast.is_none()); | ||
|
||
// Ensure the specified mock was called exactly one time (or fail with a detailed error description). | ||
storage_mock.assert(); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
async fn test_setting_storage_ok() -> Result<(), Box<dyn Error>> { | ||
let server = MockServer::start(); | ||
let storage_mock = server.mock(|when, then| { | ||
when.method(PUT) | ||
.path("/api/storage/config") | ||
.header("content-type", "application/json") | ||
.body(r#"{"legacyAutoyastStorage":{ "some" : "data" }}"#); | ||
then.status(200); | ||
}); | ||
let url = server.url("/api"); | ||
|
||
let store = storage_store(url); | ||
let boxed_raw_value = | ||
serde_json::value::RawValue::from_string(r#"{ "some" : "data" }"#.to_owned())?; | ||
let settings = StorageSettings { | ||
storage: None, | ||
storage_autoyast: Some(boxed_raw_value), | ||
}; | ||
|
||
let result = store.store(&settings).await; | ||
|
||
// main assertion | ||
result?; | ||
|
||
// Ensure the specified mock was called exactly one time (or fail with a detailed error description). | ||
storage_mock.assert(); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
------------------------------------------------------------------- | ||
Wed Sep 18 08:27:13 UTC 2024 - Martin Vidner <[email protected]> | ||
|
||
- For CLI, use HTTP clients instead of D-Bus clients, | ||
final piece: Storage (gh#openSUSE/agama#1600) | ||
- added StorageHTTPClient | ||
|
||
------------------------------------------------------------------- | ||
Wed Sep 13 12:25:28 UTC 2024 - Jorik Cronenberg <[email protected]> | ||
|
||
|