Skip to content

Commit

Permalink
feat: initial SCC proxy support (#2007)
Browse files Browse the repository at this point in the history
## Problem

Our QA team needs basic support to register against SCC proxy servers.

## Solution

* Add a new `inst.register_url` to define the URL for registering the
system.
* Take the opportunity to clean-up unused registration code.

## Testing

- Added a new unit test
- Tested manually

## Screenshots

<details>
<summary>Error using a different server</summary>

![Captura desde 2025-02-18
10-55-30](https://github.com/user-attachments/assets/2b81b93b-a751-4830-b90e-2b7fbc042fbd)
</details>
  • Loading branch information
imobachgs authored Feb 18, 2025
2 parents ca747cf + 6380a49 commit 2b74fdf
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 145 deletions.
1 change: 0 additions & 1 deletion rust/agama-lib/src/product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub mod proxies;
mod settings;
mod store;

pub use crate::software::model::RegistrationRequirement;
pub use client::{Product, ProductClient};
pub use http_client::ProductHTTPClient;
pub use settings::ProductSettings;
Expand Down
1 change: 0 additions & 1 deletion rust/agama-lib/src/product/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use std::str::FromStr;

use crate::dbus::{get_optional_property, get_property};
use crate::error::ServiceError;
use crate::software::model::RegistrationRequirement;
use crate::software::proxies::SoftwareProductProxy;
use serde::Serialize;
use zbus::Connection;
Expand Down
4 changes: 0 additions & 4 deletions rust/agama-lib/src/product/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,4 @@ pub trait Registration {
/// RegCode property
#[zbus(property)]
fn reg_code(&self) -> zbus::Result<String>;

/// Requirement property
#[zbus(property)]
fn requirement(&self) -> zbus::Result<u32>;
}
3 changes: 1 addition & 2 deletions rust/agama-lib/src/product/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ mod test {
.body(
r#"{
"key": "",
"email": "",
"requirement": "NotRequired"
"email": ""
}"#,
);
});
Expand Down
22 changes: 0 additions & 22 deletions rust/agama-lib/src/software/model/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,6 @@ pub struct RegistrationInfo {
pub email: String,
}

#[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
#[default]
No = 0,
/// Product has optional registration
Optional = 1,
/// It is mandatory to register the product
Mandatory = 2,
}

#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct RegistrationError {
/// ID of error. See dbus API for possible values
Expand Down
1 change: 0 additions & 1 deletion rust/agama-server/src/web/docs/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ impl ApiDocBuilder for SoftwareApiDocBuilder {
fn components(&self) -> Components {
ComponentsBuilder::new()
.schema_from::<agama_lib::product::Product>()
.schema_from::<agama_lib::product::RegistrationRequirement>()
.schema_from::<agama_lib::software::Pattern>()
.schema_from::<agama_lib::software::SelectedBy>()
.schema_from::<agama_lib::software::model::LanguageTag>()
Expand Down
4 changes: 0 additions & 4 deletions rust/agama-server/src/web/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use agama_lib::{
jobs::Job,
localization::model::LocaleConfig,
manager::InstallationPhase,
product::RegistrationRequirement,
progress::Progress,
software::SelectedBy,
storage::{
Expand Down Expand Up @@ -59,9 +58,6 @@ pub enum Event {
ProductChanged {
id: String,
},
RegistrationRequirementChanged {
requirement: RegistrationRequirement,
},
RegistrationChanged,
FirstUserChanged(FirstUser),
RootChanged {
Expand Down
4 changes: 4 additions & 0 deletions service/lib/agama/cmdline_args.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def initialize(data = {})
@data = data
end

def self.read
read_from("/proc/cmdline")
end

# Reads the kernel command line options
def self.read_from(path)
options = File.read(path)
Expand Down
20 changes: 0 additions & 20 deletions service/lib/agama/dbus/software/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ def select_product(id)

if code == 0
dbus_properties_changed(PRODUCT_INTERFACE, { "SelectedProduct" => id }, [])
dbus_properties_changed(REGISTRATION_INTERFACE, { "Requirement" => requirement }, [])
# FIXME: Product issues might change after selecting a product. Nevertheless,
# #on_issues_change callbacks should be used for emitting issues signals, ensuring
# they are emitted every time the backend changes its issues. Currently,
Expand All @@ -136,23 +135,6 @@ def email
backend.registration.email || ""
end

# Registration requirement.
#
# @return [Integer] Possible values:
# 0: not required
# 1: optional
# 2: mandatory
def requirement
case backend.registration.requirement
when Agama::Registration::Requirement::MANDATORY
2
when Agama::Registration::Requirement::OPTIONAL
1
else
0
end
end

# Tries to register with the given registration code.
#
# @note Software is not automatically probed after registering the product. The reason is
Expand Down Expand Up @@ -227,8 +209,6 @@ def deregister

dbus_reader(:email, "s")

dbus_reader(:requirement, "u")

dbus_method(:Register, "in reg_code:s, in options:a{sv}, out result:(us)") do |*args|
[register(args[0], email: args[1]["Email"])]
end
Expand Down
51 changes: 25 additions & 26 deletions service/lib/agama/registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
require "ostruct"
require "suse/connect"
require "y2packager/new_repository_setup"
require "agama/cmdline_args"

Yast.import "Arch"

Expand All @@ -50,12 +51,6 @@ class Registration
# @return [String, nil]
attr_reader :email

module Requirement
NO = :no
OPTIONAL = :optional
MANDATORY = :mandatory
end

# @param software_manager [Agama::Software::Manager]
# @param logger [Logger]
def initialize(software_manager, logger)
Expand All @@ -76,12 +71,9 @@ def initialize(software_manager, logger)
def register(code, email: "")
return if product.nil? || reg_code

connect_params = {
token: code,
email: email
}
reg_params = connect_params(token: code, email: email)

login, password = SUSE::Connect::YaST.announce_system(connect_params, target_distro)
login, password = SUSE::Connect::YaST.announce_system(reg_params, target_distro)
# write the global credentials
# TODO: check if we can do it in memory for libzypp
SUSE::Connect::YaST.create_credentials_file(login, password, GLOBAL_CREDENTIALS_PATH)
Expand Down Expand Up @@ -122,11 +114,8 @@ def deregister
Y2Packager::NewRepositorySetup.instance.services.delete(@service.name)
@software.remove_service(@service)

connect_params = {
token: reg_code,
email: email
}
SUSE::Connect::YaST.deactivate_system(connect_params)
reg_params = connect_params(token: reg_code, email: email)
SUSE::Connect::YaST.deactivate_system(reg_params)
FileUtils.rm(GLOBAL_CREDENTIALS_PATH) # connect does not remove it itself
if @credentials_file
FileUtils.rm(File.join(TARGET_DIR, credentials_path(@credentials_file)))
Expand Down Expand Up @@ -157,16 +146,6 @@ def finish
end
end

# Indicates whether the registration is optional, mandatory or not required.
#
# @return [Symbol] See {Requirement}.
def requirement
return Requirement::NO unless product
return Requirement::MANDATORY if product.repositories.none?

Requirement::NO
end

# Callbacks to be called when registration changes (e.g., a different product is selected).
def on_change(&block)
@on_change_callbacks ||= []
Expand Down Expand Up @@ -211,5 +190,25 @@ def credentials_from_url(url)
def credentials_path(file)
File.join(SUSE::Connect::YaST::DEFAULT_CREDENTIALS_DIR, file)
end

# Returns the arguments to connect to the registration server
#
# @param params [Hash] additional parameters (e.g., email and token)
# @return [Hash]
def connect_params(params = {})
default_params = {}
default_params[:url] = registration_url if registration_url
default_params.merge(params)
end

# Returns the URL of the registration server
#
# At this point, it just checks the kernel's command-line.
#
# @return [String, nil]
def registration_url
cmdline_args = CmdlineArgs.read
cmdline_args.data["register_url"]
end
end
end
5 changes: 5 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
-------------------------------------------------------------------
Tue Feb 18 11:27:04 UTC 2025 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Implement basic support for RMT/SCC proxies (gh#agama-project/agama#2007).

-------------------------------------------------------------------
Mon Feb 17 09:17:47 UTC 2025 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
30 changes: 0 additions & 30 deletions service/test/agama/dbus/software/product_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,36 +152,6 @@
end
end

describe "#requirement" do
before do
allow(backend.registration).to receive(:requirement).and_return(requirement)
end

context "if the registration is not required" do
let(:requirement) { Agama::Registration::Requirement::NO }

it "returns 0" do
expect(subject.requirement).to eq(0)
end
end

context "if the registration is optional" do
let(:requirement) { Agama::Registration::Requirement::OPTIONAL }

it "returns 1" do
expect(subject.requirement).to eq(1)
end
end

context "if the registration is mandatory" do
let(:requirement) { Agama::Registration::Requirement::MANDATORY }

it "returns 2" do
expect(subject.requirement).to eq(2)
end
end
end

describe "#register" do
before do
allow(backend.registration).to receive(:reg_code).and_return(nil)
Expand Down
49 changes: 17 additions & 32 deletions service/test/agama/registration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@
allow(SUSE::Connect::YaST).to receive(:create_credentials_file)
allow(SUSE::Connect::YaST).to receive(:activate_product).and_return(service)
allow(Y2Packager::NewRepositorySetup.instance).to receive(:add_service)
allow(Agama::CmdlineArgs).to receive(:read).and_return(cmdline_args)
end

let(:service) { OpenStruct.new(name: "test-service", url: nil) }
let(:cmdline_args) { Agama::CmdlineArgs.new({}) }

describe "#register" do
context "if there is no product selected yet" do
Expand Down Expand Up @@ -89,6 +91,21 @@
subject.register("11112222", email: "test@test.com")
end

context "when a registration URL is set through the cmdline" do
let(:cmdline_args) do
Agama::CmdlineArgs.new("register_url" => "http://scc.example.net")
end

it "registers using the given URL" do
expect(SUSE::Connect::YaST).to receive(:announce_system).with(
{ token: "11112222", email: "test@test.com", url: "http://scc.example.net" },
"test-5-x86_64"
)

subject.register("11112222", email: "test@test.com")
end
end

it "creates credentials file" do
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
.with("test-user", "12345", "/etc/zypp/credentials.d/SCCcredentials")
Expand Down Expand Up @@ -345,38 +362,6 @@
end
end

describe "#requirement" do
context "if there is not product selected yet" do
let(:product) { nil }

it "returns not required" do
expect(subject.requirement).to eq(Agama::Registration::Requirement::NO)
end
end

context "if there is a selected product" do
let(:product) do
Agama::Software::Product.new("test").tap { |p| p.repositories = repositories }
end

context "and the product has repositories" do
let(:repositories) { ["https://repo"] }

it "returns not required" do
expect(subject.requirement).to eq(Agama::Registration::Requirement::NO)
end
end

context "and the product has no repositories" do
let(:repositories) { [] }

it "returns mandatory" do
expect(subject.requirement).to eq(Agama::Registration::Requirement::MANDATORY)
end
end
end
end

describe "#finish" do
context "system is not registered" do
before do
Expand Down
2 changes: 0 additions & 2 deletions web/src/types/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
*/

type Registration = {
/** Registration requirement (i.e., "not-required", "optional", "mandatory") */
requirement: "no" | "optional" | "mandatory";
/** Registration code, if any */
code?: string;
/** Registration email, if any */
Expand Down

0 comments on commit 2b74fdf

Please sign in to comment.