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

fix: Emit signal when storage propeties change #1236

Merged
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
35 changes: 24 additions & 11 deletions service/lib/agama/dbus/clients/base.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) [2022-2023] SUSE LLC
# Copyright (c) [2022-2024] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -68,24 +68,37 @@ def service
# @return [Logger]
attr_reader :logger

# Registers callback to be called when the properties of the given object changes
# Registers a callback to be called when the properties of the given object changes.
#
# @param dbus_object [::DBus::Object]
# @param block [Proc]
def on_properties_change(dbus_object, &block)
subscribe(dbus_object, "org.freedesktop.DBus.Properties", "PropertiesChanged", &block)
end

# Subscribes to a D-Bus signal.
#
# @note Signal subscription is done only once. Otherwise, the latest subscription overrides
# the previous one.
#
# @param dbus_object [::DBus::Object]
# @param interface [String]
# @param signal [String]
# @param block [Proc]
def on_properties_change(dbus_object, &block)
@on_properties_change_callbacks ||= {}
@on_properties_change_callbacks[dbus_object.path] ||= []
@on_properties_change_callbacks[dbus_object.path] << block
def subscribe(dbus_object, interface, signal, &block)
@signal_handlers ||= {}
@signal_handlers[dbus_object.path] ||= {}
@signal_handlers[dbus_object.path][interface] ||= {}
@signal_handlers[dbus_object.path][interface][signal] ||= []
@signal_handlers[dbus_object.path][interface][signal] << block

callbacks = @signal_handlers.dig(dbus_object.path, interface, signal)

return if @on_properties_change_callbacks[dbus_object.path].size > 1
return if callbacks.size > 1

dbus_properties = dbus_object["org.freedesktop.DBus.Properties"]
dbus_properties.on_signal("PropertiesChanged") do |interface, changes, invalid|
callbacks = @on_properties_change_callbacks[dbus_object.path]
callbacks.each { |c| c.call(interface, changes, invalid) }
interface_proxy = dbus_object[interface]
interface_proxy.on_signal(signal) do |*args|
callbacks.each { |c| c.call(*args) }
end
end

Expand Down
38 changes: 24 additions & 14 deletions service/lib/agama/dbus/clients/software.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,10 @@ class Software < Base
TYPES = [:package, :pattern].freeze
private_constant :TYPES

def initialize
super

@dbus_object = service["/org/opensuse/Agama/Software1"]
@dbus_object.introspect

@dbus_product = service["/org/opensuse/Agama/Software1/Product"]
@dbus_product.introspect

@dbus_proposal = service["/org/opensuse/Agama/Software1/Proposal"]
@dbus_proposal.introspect
# @note This client is singleton because ruby-dbus does not work properly with several
# instances of the same client.
def self.instance
@instance ||= new
end

# @return [String]
Expand Down Expand Up @@ -176,9 +169,6 @@ def remove_resolvables(unique_id, type, resolvables, optional: false)

# Registers a callback to run when the product changes
#
# @note Signal subscription is done only once. Otherwise, the latest subscription overrides
# the previous one.
#
# @param block [Proc] Callback to run when a product is selected
def on_product_selected(&block)
on_properties_change(dbus_product) do |_, changes, _|
Expand All @@ -187,6 +177,13 @@ def on_product_selected(&block)
end
end

# Registers a callback to run when the software is probed.
#
# @param block [Proc]
def on_probe_finished(&block)
subscribe(dbus_object, "org.opensuse.Agama.Software1", "ProbeFinished", &block)
end

private

# @return [::DBus::Object]
Expand All @@ -197,6 +194,19 @@ def on_product_selected(&block)

# @return [::DBus::Object]
attr_reader :dbus_proposal

def initialize
super

@dbus_object = service["/org/opensuse/Agama/Software1"]
@dbus_object.introspect

@dbus_product = service["/org/opensuse/Agama/Software1/Product"]
@dbus_product.introspect

@dbus_proposal = service["/org/opensuse/Agama/Software1/Proposal"]
@dbus_proposal.introspect
end
end
end
end
Expand Down
3 changes: 3 additions & 0 deletions service/lib/agama/dbus/software/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def issues

dbus_method(:UsedDiskSpace, "out SpaceSize:s") { backend.used_disk_space }

dbus_signal(:ProbeFinished)

dbus_method(:Probe) { probe }
dbus_method(:Propose) { propose }
dbus_method(:Install) { install }
Expand All @@ -115,6 +117,7 @@ def issues

def probe
busy_while { backend.probe }
self.ProbeFinished
end

def propose
Expand Down
39 changes: 31 additions & 8 deletions service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ class Manager < BaseObject
def initialize(backend, logger)
super(PATH, logger: logger)
@backend = backend
@product_mount_points = read_product_mount_points
@encryption_methods = read_encryption_methods

register_storage_callbacks
register_proposal_callbacks
register_progress_callbacks
register_service_status_callbacks
register_iscsi_callbacks
register_software_callbacks

add_s390_interfaces if Yast::Arch.s390
end
Expand Down Expand Up @@ -118,18 +122,23 @@ def available_devices
proposal.available_devices.map { |d| system_devices_tree.path_for(d) }
end

# List of meaningful mount points for the current product.
# Reads the list of meaningful mount points for the current product.
#
# @return [Array<String>]
def product_mount_points
volume_templates_builder.all.map(&:mount_path).reject(&:empty?)
def read_product_mount_points
volume_templates_builder
.all
.map(&:mount_path)
.reject(&:empty?)
end

# List of possible encryption methods for the current system and product
# Reads the list of possible encryption methods for the current system and product.
#
# @return [Array<String>]
def encryption_methods
Agama::Storage::EncryptionSettings.available_methods.map { |m| m.id.to_s }
def read_encryption_methods
Agama::Storage::EncryptionSettings
.available_methods
.map { |m| m.id.to_s }
end

# Path of the D-Bus object containing the calculated proposal
Expand Down Expand Up @@ -169,9 +178,11 @@ def calculate_proposal(dbus_settings)
dbus_interface PROPOSAL_CALCULATOR_INTERFACE do
dbus_reader :available_devices, "ao"

dbus_reader :product_mount_points, "as"
# PropertiesChanged signal if the product changes, see {#register_software_callbacks}.
dbus_reader_attr_accessor :product_mount_points, "as"

dbus_reader :encryption_methods, "as"
# PropertiesChanged signal if software is probed, see {#register_software_callbacks}.
dbus_reader_attr_accessor :encryption_methods, "as"

dbus_reader :result, "o"

Expand Down Expand Up @@ -310,6 +321,18 @@ def register_iscsi_callbacks
end
end

def register_software_callbacks
backend.software.on_product_selected do
# A PropertiesChanged signal is emitted (see ::DBus::Object.dbus_reader_attr_accessor).
self.product_mount_points = read_product_mount_points
end

backend.software.on_probe_finished do
# A PropertiesChanged signal is emitted (see ::DBus::Object.dbus_reader_attr_accessor).
self.encryption_methods = read_encryption_methods
end
end

def storage_properties_changed
properties = interfaces_and_properties[STORAGE_INTERFACE]
dbus_properties_changed(STORAGE_INTERFACE, properties, [])
Expand Down
2 changes: 1 addition & 1 deletion service/lib/agama/dbus/y2dir/manager/modules/Package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ module Yast
class PackageClass < Module
def main
puts "Loading mocked module #{__FILE__}"
@client = Agama::DBus::Clients::Software.new
@client = Agama::DBus::Clients::Software.instance
end

# Determines whether a package is available.
Expand Down
4 changes: 2 additions & 2 deletions service/lib/agama/storage/manager.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) [2022-2023] SUSE LLC
# Copyright (c) [2022-2024] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -157,7 +157,7 @@ def iscsi
#
# @return [Agama::DBus::Clients::Software]
def software
@software ||= DBus::Clients::Software.new
@software ||= DBus::Clients::Software.instance
end

private
Expand Down
7 changes: 7 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Tue May 21 05:32:46 UTC 2024 - José Iván López González <jlopez@suse.com>

- Emit a PropertiesChanged signal for ProductMountPoints and
EncryptionMethods properties when the product changes
(gh#openSUSE/agama#1236).

-------------------------------------------------------------------
Fri May 17 09:52:25 UTC 2024 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
25 changes: 25 additions & 0 deletions service/test/agama/dbus/clients/software_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@
end
end

describe "#on_probe_finished" do
before do
allow(dbus_object).to receive(:path).and_return("/org/opensuse/Agama/Test")
allow(software_iface).to receive(:on_signal)
end

context "if there are no callbacks for the signal" do
it "subscribes to the signal" do
expect(software_iface).to receive(:on_signal)
subject.on_probe_finished { "test" }
end
end

context "if there already are callbacks for the signal" do
before do
subject.on_probe_finished { "test" }
end

it "does not subscribe to the signal again" do
expect(software_iface).to_not receive(:on_signal)
subject.on_probe_finished { "test" }
end
end
end

include_examples "issues"
include_examples "service status"
include_examples "progress"
Expand Down
3 changes: 2 additions & 1 deletion service/test/agama/dbus/software/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@
end

describe "#probe" do
it "runs the probing, setting the service as busy meanwhile" do
it "runs the probing, setting the service as busy meanwhile, and emits a signal" do
expect(subject.service_status).to receive(:busy)
expect(backend).to receive(:probe)
expect(subject.service_status).to receive(:idle)
expect(subject).to receive(:ProbeFinished)

subject.probe
end
Expand Down
6 changes: 5 additions & 1 deletion service/test/agama/dbus/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@
on_sessions_change: nil)
end

let(:software) { instance_double(Agama::DBus::Clients::Software, on_product_selected: nil) }
let(:software) do
instance_double(Agama::DBus::Clients::Software,
on_product_selected: nil,
on_probe_finished: nil)
end

before do
allow(Yast::Arch).to receive(:s390).and_return false
Expand Down
6 changes: 2 additions & 4 deletions service/test/agama/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@

before do
allow(Agama::DBus::Clients::Questions).to receive(:new).and_return(questions_client)
allow(Agama::DBus::Clients::Software).to receive(:new)
.and_return(software)
allow(Bootloader::FinishClient).to receive(:new)
.and_return(bootloader_finish)
allow(Agama::DBus::Clients::Software).to receive(:instance).and_return(software)
allow(Bootloader::FinishClient).to receive(:new).and_return(bootloader_finish)
allow(Agama::Security).to receive(:new).and_return(security)
end

Expand Down
Loading