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

feat: implement product registration support #1809

Merged
merged 54 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6abef5d
feat(products): add a SLES 15.6 definition
imobachgs Dec 3, 2024
c593d0f
fix(service): fix wrong method name
imobachgs Dec 3, 2024
fc4da53
feat(service): include registration requirement in the product
imobachgs Dec 4, 2024
68dad4c
feat(rust): include registration requirement in the product
imobachgs Dec 4, 2024
fbbb7a7
feat(products): add registration requirement to SLE 15 SP6
imobachgs Dec 4, 2024
d0beb0b
feat(service): move the "version" field to the top-level
imobachgs Dec 4, 2024
bf7d346
refactor(rust): change registration "not_required" to "no"
imobachgs Dec 4, 2024
9aac426
chore(products): temporarily move SLE 15 definition to openSUSE package
imobachgs Dec 4, 2024
9906fc0
chore(products): swap SLE 15 and SLE 16 definitions
imobachgs Dec 4, 2024
22b734d
quick POC to fix registration:
jreidinger Dec 4, 2024
b9c0155
make rubocop happy and fix tests
jreidinger Dec 4, 2024
3e3708c
fix(web): add registration to Product type
dgdavid Dec 4, 2024
56fa368
fix creating credentials
jreidinger Dec 4, 2024
916d41e
feat(web): allow setting a route only for registrable products
dgdavid Dec 4, 2024
34d311d
feat(web): add product registration interface
dgdavid Dec 4, 2024
0063a08
fix(web): use "supportive paths" for InstallButton
dgdavid Dec 4, 2024
5aec29f
quick POC to fix registration: (#1807)
imobachgs Dec 4, 2024
64d27a3
fix(service): do not initialize the target in add_service
imobachgs Dec 4, 2024
656091e
fix(web): temporarily disable a TS problem
imobachgs Dec 4, 2024
945b00f
fix reading global credentials file
jreidinger Dec 5, 2024
35f303f
fix(web): mount registration alert only in valid paths
dgdavid Dec 5, 2024
8a886df
Merge remote-tracking branch 'origin/product-registration' into produ…
jreidinger Dec 5, 2024
f385dda
adapt tests for workaround
jreidinger Dec 5, 2024
7997a40
fix reading global credentials file (#1810)
imobachgs Dec 5, 2024
f532b37
fix: use registration requirement capitalization
imobachgs Dec 5, 2024
1d6c5a9
fix(web): adapt the registration alert condition
imobachgs Dec 5, 2024
0bb69a7
fix(web): show registration alert in all sections
dgdavid Dec 5, 2024
42ad4a2
fix(web): adapt broken registration conditions
dgdavid Dec 5, 2024
a923a26
fix(web): update tests for core/Page.Content
dgdavid Dec 5, 2024
564d7cf
fix(web): add basic tests for registration page
dgdavid Dec 5, 2024
12cbb83
fix(web) minor changes for overview page
dgdavid Dec 5, 2024
5cd947b
fix(web) make test suite work again
dgdavid Dec 5, 2024
7c3bd3b
fix(web): make test suite work again
dgdavid Dec 5, 2024
c14bccc
fix(web): fix product registration values
dgdavid Dec 5, 2024
59c389c
Merge branch 'master' into product-registration
imobachgs Dec 13, 2024
123a751
feat(rust): remove the SLES 15.6 definition
imobachgs Dec 13, 2024
8287710
feat(rust): enable the registration for SLE 16
imobachgs Dec 13, 2024
4ec593a
fix(web): remove an unused variable
imobachgs Dec 13, 2024
3135178
feat(products): enable registration for SLES for SAP
imobachgs Dec 13, 2024
8182c1c
fix(web): make ESLint happy
imobachgs Dec 13, 2024
ba20646
Merge remote-tracking branch 'origin/master' into merge_master_reg
jreidinger Jan 7, 2025
6fd4bda
fix(web): please TypeScript
dgdavid Jan 7, 2025
6d0919e
fix(web): do not show the registration alert when registration type u…
dgdavid Jan 7, 2025
52f4da9
Merge master reg (#1864)
dgdavid Jan 7, 2025
7e4e6db
fix id to match scc
jreidinger Jan 8, 2025
9a98871
fix(rust): handle registration errors properly
imobachgs Jan 8, 2025
9807c91
allow sles4SAP only on supported architectures
jreidinger Jan 8, 2025
3850472
doc(rust): update changes files
imobachgs Jan 8, 2025
0cedb6a
fix product registration finish
jreidinger Jan 8, 2025
d66322c
make rubocop happy
jreidinger Jan 8, 2025
6ffe7c2
Merge remote-tracking branch 'origin/master' into product-registration
jreidinger Jan 8, 2025
83a8c0d
fix(web): fix duplicated variable in ProductSelectionPage tests
imobachgs Jan 8, 2025
2c87855
copy proper location of product credentials
jreidinger Jan 9, 2025
5014eff
make rubocop happy
jreidinger Jan 9, 2025
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
7 changes: 6 additions & 1 deletion products.d/agama-products.changes
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
-------------------------------------------------------------------
Wed Jan 8 14:07:21 UTC 2025 - Imobach Gonzalez Sosa <[email protected]>

- Add support for products registration (jsc#PED-11192,
gh#agama-project/agama#1809).

-------------------------------------------------------------------
Tue Jan 7 12:57:13 UTC 2025 - Lubos Kocman <[email protected]>

- Drop yast from Leap 16.0 software selection
code-o-o#leap/features#173


-------------------------------------------------------------------
Mon Jan 6 14:41:28 UTC 2025 - Angela Briel <[email protected]>

Expand Down
17 changes: 4 additions & 13 deletions products.d/sles_160.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
id: SLES_16.0
id: SLES
name: SUSE Linux Enterprise Server 16.0 Alpha
registration: "mandatory"
version: "16-0"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
Expand Down Expand Up @@ -50,18 +52,7 @@ translations:
altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
software:
installation_repositories:
# Use plain HTTP repositories, HTTPS does not work without importing the SSL
# certificate. It is safe as the repository is GPG checked and you neeed VPN
# to reach the internal server anyway.
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-x86_64/
archs: x86_64
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-aarch64/
archs: aarch64
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-ppc64le/
archs: ppc
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-s390x/
archs: s390
installation_repositories: []

mandatory_patterns:
- base_traditional
Expand Down
18 changes: 5 additions & 13 deletions products.d/sles_sap_160.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
id: SLES_SAP_16.0
id: SLES-SAP
name: SUSE Linux Enterprise Server for SAP Applications 16.0 Beta
archs: x86_64,aarch64,ppc
registration: "mandatory"
version: "16-0"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
Expand All @@ -14,18 +17,7 @@ icon: SUSE.svg
translations:
description:
software:
installation_repositories:
# Use plain HTTP repositories, HTTPS does not work without importing the SSL
# certificate. It is safe as the repository is GPG checked and you neeed VPN
# to reach the internal server anyway.
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-x86_64/
archs: x86_64
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-aarch64/
archs: aarch64
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-ppc64le/
archs: ppc
- url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-s390x/
archs: s390
installation_repositories: []

mandatory_patterns:
- base_traditional
Expand Down
17 changes: 10 additions & 7 deletions rust/agama-lib/src/product/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
// find current contact information at www.suse.com.

use std::collections::HashMap;
use std::str::FromStr;

use crate::dbus::get_property;
use crate::error::ServiceError;
use crate::software::model::RegistrationRequirement;
use crate::software::proxies::SoftwareProductProxy;
Expand All @@ -39,6 +41,8 @@ pub struct Product {
pub description: String,
/// Product icon (e.g., "default.svg")
pub icon: String,
/// Registration requirement
pub registration: RegistrationRequirement,
}

/// D-Bus client for the software service
Expand Down Expand Up @@ -72,11 +76,17 @@ impl<'a> ProductClient<'a> {
Some(value) => value.try_into().unwrap(),
None => "default.svg",
};

let registration = get_property::<String>(&data, "registration")
.map(|r| RegistrationRequirement::from_str(&r).unwrap_or_default())
.unwrap_or_default();

Product {
id,
name,
description: description.to_string(),
icon: icon.to_string(),
registration,
}
})
.collect();
Expand Down Expand Up @@ -114,13 +124,6 @@ impl<'a> ProductClient<'a> {
Ok(self.registration_proxy.email().await?)
}

pub async fn registration_requirement(&self) -> Result<RegistrationRequirement, ServiceError> {
let requirement = self.registration_proxy.requirement().await?;
// unknown number can happen only if we do programmer mistake
let result: RegistrationRequirement = requirement.try_into().unwrap();
Ok(result)
}

/// register product
pub async fn register(&self, code: &str, email: &str) -> Result<(u32, String), ServiceError> {
let mut options: HashMap<&str, &zbus::zvariant::Value> = HashMap::new();
Expand Down
21 changes: 19 additions & 2 deletions rust/agama-lib/src/product/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use crate::software::model::RegistrationError;
use crate::software::model::RegistrationInfo;
use crate::software::model::RegistrationParams;
use crate::software::model::SoftwareConfig;
Expand Down Expand Up @@ -64,13 +65,29 @@ impl ProductHTTPClient {
}

/// register product
pub async fn register(&self, key: &str, email: &str) -> Result<(u32, String), ServiceError> {
pub async fn register(&self, key: &str, email: &str) -> Result<(), ServiceError> {
// note RegistrationParams != RegistrationInfo, fun!
let params = RegistrationParams {
key: key.to_owned(),
email: email.to_owned(),
};
let result = self
.client
.post_void("/software/registration", &params)
.await;

self.client.post("/software/registration", &params).await
let Err(error) = result else {
return Ok(());
};

let message = match error {
ServiceError::BackendError(_, details) => {
let details: RegistrationError = serde_json::from_str(&details).unwrap();
format!("{} (error code: {})", details.message, details.id)
}
_ => format!("Could not register the product: #{error:?}"),
};

Err(ServiceError::FailedRegistration(message))
}
}
13 changes: 2 additions & 11 deletions rust/agama-lib/src/product/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,8 @@ impl ProductStore {
}
}
if let Some(reg_code) = &settings.registration_code {
let (result, message);
if let Some(email) = &settings.registration_email {
(result, message) = self.product_client.register(reg_code, email).await?;
} else {
(result, message) = self.product_client.register(reg_code, "").await?;
}
// FIXME: name the magic numbers. 3 is Registration not required
// FIXME: well don't register when not required (no regcode in profile)
if result != 0 && result != 3 {
return Err(ServiceError::FailedRegistration(message));
}
let email = settings.registration_email.as_deref().unwrap_or("");
self.product_client.register(reg_code, email).await?;
probe = true;
}

Expand Down
42 changes: 20 additions & 22 deletions rust/agama-lib/src/software/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,36 @@ pub struct RegistrationInfo {
pub key: String,
/// Registration email. Empty value mean email not used or not registered.
pub email: String,
/// if registration is required, optional or not needed for current product.
/// Change only if selected product is changed.
pub requirement: RegistrationRequirement,
}

#[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(
Clone,
Default,
Debug,
Serialize,
Deserialize,
strum::Display,
strum::EnumString,
utoipa::ToSchema,
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub enum RegistrationRequirement {
/// Product does not require registration
NotRequired = 0,
#[default]
No = 0,
/// Product has optional registration
Optional = 1,
/// It is mandatory to register the product
Mandatory = 2,
}

impl TryFrom<u32> for RegistrationRequirement {
type Error = ();

fn try_from(v: u32) -> Result<Self, Self::Error> {
match v {
x if x == RegistrationRequirement::NotRequired as u32 => {
Ok(RegistrationRequirement::NotRequired)
}
x if x == RegistrationRequirement::Optional as u32 => {
Ok(RegistrationRequirement::Optional)
}
x if x == RegistrationRequirement::Mandatory as u32 => {
Ok(RegistrationRequirement::Mandatory)
}
_ => Err(()),
}
}
#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct RegistrationError {
/// ID of error. See dbus API for possible values
pub id: u32,
/// human readable error string intended to be displayed to user
pub message: String,
}

/// Software resolvable type (package or pattern).
Expand Down
49 changes: 9 additions & 40 deletions rust/agama-server/src/software/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ use agama_lib::{
error::ServiceError,
product::{proxies::RegistrationProxy, Product, ProductClient},
software::{
model::{RegistrationInfo, RegistrationParams, ResolvableParams, SoftwareConfig},
model::{
RegistrationError, RegistrationInfo, RegistrationParams, ResolvableParams,
SoftwareConfig,
},
proxies::{Software1Proxy, SoftwareProductProxy},
Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy,
},
Expand All @@ -49,7 +52,7 @@ use axum::{
routing::{get, post, put},
Json, Router,
};
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::collections::HashMap;
use tokio_stream::{Stream, StreamExt};

Expand All @@ -74,10 +77,6 @@ pub async fn software_streams(dbus: zbus::Connection) -> Result<EventStreams, Er
"product_changed",
Box::pin(product_changed_stream(dbus.clone()).await?),
),
(
"registration_requirement_changed",
Box::pin(registration_requirement_changed_stream(dbus.clone()).await?),
),
(
"registration_code_changed",
Box::pin(registration_code_changed_stream(dbus.clone()).await?),
Expand Down Expand Up @@ -131,27 +130,6 @@ async fn patterns_changed_stream(
Ok(stream)
}

async fn registration_requirement_changed_stream(
dbus: zbus::Connection,
) -> Result<impl Stream<Item = Event>, Error> {
// TODO: move registration requirement to product in dbus and so just one event will be needed.
let proxy = RegistrationProxy::new(&dbus).await?;
let stream = proxy
.receive_requirement_changed()
.await
.then(|change| async move {
if let Ok(id) = change.get().await {
// unwrap is safe as possible numbers is send by our controlled dbus
return Some(Event::RegistrationRequirementChanged {
requirement: id.try_into().unwrap(),
});
}
None
})
.filter_map(|e| e);
Ok(stream)
}

async fn registration_email_changed_stream(
dbus: zbus::Connection,
) -> Result<impl Stream<Item = Event>, Error> {
Expand Down Expand Up @@ -269,19 +247,10 @@ async fn get_registration(
let result = RegistrationInfo {
key: state.product.registration_code().await?,
email: state.product.email().await?,
requirement: state.product.registration_requirement().await?,
};
Ok(Json(result))
}

#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct FailureDetails {
/// ID of error. See dbus API for possible values
id: u32,
/// human readable error string intended to be displayed to user
message: String,
}

/// Register product
///
/// * `state`: service state.
Expand All @@ -291,7 +260,7 @@ pub struct FailureDetails {
context_path = "/api/software",
responses(
(status = 204, description = "registration successfull"),
(status = 422, description = "Registration failed. Details are in body", body = FailureDetails),
(status = 422, description = "Registration failed. Details are in body", body = RegistrationError),
(status = 400, description = "The D-Bus service could not perform the action")
)
)]
Expand All @@ -300,10 +269,10 @@ async fn register(
Json(config): Json<RegistrationParams>,
) -> Result<impl IntoResponse, Error> {
let (id, message) = state.product.register(&config.key, &config.email).await?;
let details = FailureDetails { id, message };
if id == 0 {
Ok((StatusCode::NO_CONTENT, ().into_response()))
} else {
let details = RegistrationError { id, message };
Ok((
StatusCode::UNPROCESSABLE_ENTITY,
Json(details).into_response(),
Expand All @@ -320,13 +289,13 @@ async fn register(
context_path = "/api/software",
responses(
(status = 200, description = "deregistration successfull"),
(status = 422, description = "De-registration failed. Details are in body", body = FailureDetails),
(status = 422, description = "De-registration failed. Details are in body", body = RegistrationError),
(status = 400, description = "The D-Bus service could not perform the action")
)
)]
async fn deregister(State(state): State<SoftwareState<'_>>) -> Result<impl IntoResponse, Error> {
let (id, message) = state.product.deregister().await?;
let details = FailureDetails { id, message };
let details = RegistrationError { id, message };
if id == 0 {
Ok((StatusCode::NO_CONTENT, ().into_response()))
} else {
Expand Down
6 changes: 6 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Wed Jan 8 14:05:34 UTC 2025 - Imobach Gonzalez Sosa <[email protected]>

- Add support for products registration (jsc#PED-11192,
gh#agama-project/agama#1809).

-------------------------------------------------------------------
Fri Dec 20 12:17:26 UTC 2024 - Josef Reidinger <[email protected]>

Expand Down
7 changes: 4 additions & 3 deletions service/lib/agama/dbus/software/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ def available_products
product.id,
product.display_name,
{
"description" => product.localized_description,
"icon" => product.icon
"description" => product.localized_description,
"icon" => product.icon,
"registration" => product.registration
}
]
end
Expand Down Expand Up @@ -177,7 +178,7 @@ def register(reg_code, email: nil)
[1, "Product not selected yet"]
elsif backend.registration.reg_code
[2, "Product already registered"]
elsif backend.registration.requirement == Agama::Registration::Requirement::NOT_REQUIRED
elsif backend.registration.requirement == Agama::Registration::Requirement::NO
[3, "Product does not require registration"]
else
connect_result(first_error_code: 4) do
Expand Down
Loading
Loading