diff --git a/cli/Gemfile.lock b/cli/Gemfile.lock index 7d66f79a32..34d13bc530 100644 --- a/cli/Gemfile.lock +++ b/cli/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../service specs: - d-installer (0.2) + d-installer (0.3) cfa (~> 1.0.2) cfa_grub2 (~> 2.0.0) cheetah (~> 1.0.0) @@ -14,8 +14,8 @@ PATH PATH remote: . specs: - d-installer-cli (0.2) - d-installer (= 0.2) + d-installer-cli (0.3) + d-installer (= 0.3) fast_gettext (~> 2.2.0) ruby-dbus (>= 0.18.0.beta5) thor (~> 1.2, >= 1.2.1) @@ -56,7 +56,7 @@ GEM rspec-support (~> 3.11.0) rspec-support (3.11.0) ruby-augeas (0.5.0) - ruby-dbus (0.18.0.beta6) + ruby-dbus (0.18.0.beta7) rexml simplecov (0.21.2) docile (~> 1.1) diff --git a/cli/lib/dinstaller_cli/clients.rb b/cli/lib/dinstaller_cli/clients.rb index ef7b604659..f786e18f7d 100644 --- a/cli/lib/dinstaller_cli/clients.rb +++ b/cli/lib/dinstaller_cli/clients.rb @@ -27,5 +27,4 @@ module Clients require "dinstaller_cli/clients/manager" require "dinstaller_cli/clients/language" -require "dinstaller_cli/clients/software" require "dinstaller_cli/clients/storage" diff --git a/cli/lib/dinstaller_cli/clients/software.rb b/cli/lib/dinstaller_cli/clients/software.rb deleted file mode 100644 index a541b4ee86..0000000000 --- a/cli/lib/dinstaller_cli/clients/software.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -# Copyright (c) [2022] SUSE LLC -# -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of version 2 of the GNU General Public License as published -# by the Free Software Foundation. -# -# 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. - -require "dbus" - -module DInstallerCli - module Clients - # D-Bus client for software configuration - class Software - def initialize - @dbus_object = service.object("/org/opensuse/DInstaller/Software1") - @dbus_object.introspect - end - - # Available products for the installation - # - # @return [Array>] name and display name of each product - def available_products - dbus_object["org.opensuse.DInstaller.Software1"]["AvailableBaseProducts"].map do |l| - l[0..1] - end - end - - # Product selected to install - # - # @return [String] name of the product - def selected_product - dbus_object["org.opensuse.DInstaller.Software1"]["SelectedBaseProduct"] - end - - # Selects the product to install - # - # @param name [String] - def select_product(name) - dbus_object.SelectProduct(name) - end - - private - - # @return [::DBus::Object] - attr_reader :dbus_object - - # @return [::DBus::Service] - def service - @service ||= bus.service("org.opensuse.DInstaller") - end - - def bus - @bus ||= DBus::SystemBus.instance - end - end - end -end diff --git a/cli/lib/dinstaller_cli/commands/config.rb b/cli/lib/dinstaller_cli/commands/config.rb index e8a918ac60..a3f1fd5f2e 100644 --- a/cli/lib/dinstaller_cli/commands/config.rb +++ b/cli/lib/dinstaller_cli/commands/config.rb @@ -23,8 +23,8 @@ require "dinstaller_cli/install_config" require "dinstaller_cli/install_config_reader" require "dinstaller_cli/clients/language" -require "dinstaller_cli/clients/software" require "dinstaller_cli/clients/storage" +require "dinstaller/dbus/clients/software" require "dinstaller/dbus/clients/users" module DInstallerCli @@ -135,7 +135,7 @@ def language_client end def software_client - @software_client ||= Clients::Software.new + @software_client ||= DInstaller::DBus::Clients::Software.new end def storage_client diff --git a/cli/lib/dinstaller_cli/commands/software.rb b/cli/lib/dinstaller_cli/commands/software.rb index 90748dbae7..6c5335657f 100644 --- a/cli/lib/dinstaller_cli/commands/software.rb +++ b/cli/lib/dinstaller_cli/commands/software.rb @@ -19,29 +19,8 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -# frozen_string_literal: true - -# Copyright (c) [2022] SUSE LLC -# -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of version 2 of the GNU General Public License as published -# by the Free Software Foundation. -# -# 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. - require "thor" -require "dinstaller_cli/clients/software" +require "dinstaller/dbus/clients/software" module DInstallerCli module Commands @@ -63,7 +42,7 @@ def selected_product(id = nil) private def client - @client ||= Clients::Software.new + @client ||= DInstaller::DBus::Clients::Software.new end end end diff --git a/cli/test/dinstaller_cli/commands/config_test.rb b/cli/test/dinstaller_cli/commands/config_test.rb index 6cfa197b2d..7194f62519 100644 --- a/cli/test/dinstaller_cli/commands/config_test.rb +++ b/cli/test/dinstaller_cli/commands/config_test.rb @@ -24,19 +24,19 @@ require "dinstaller_cli/install_config" require "dinstaller_cli/install_config_reader" require "dinstaller_cli/clients/language" -require "dinstaller_cli/clients/software" require "dinstaller_cli/clients/storage" require "dinstaller/dbus/clients/users" +require "dinstaller/dbus/clients/software" describe DInstallerCli::Commands::Config do before do - allow(DInstallerCli::Clients::Software).to receive(:new).and_return(software_client) + allow(DInstaller::DBus::Clients::Software).to receive(:new).and_return(software_client) allow(DInstallerCli::Clients::Language).to receive(:new).and_return(language_client) allow(DInstallerCli::Clients::Storage).to receive(:new).and_return(storage_client) allow(DInstaller::DBus::Clients::Users).to receive(:new).and_return(users_client) end - let(:software_client) { instance_double(DInstallerCli::Clients::Software) } + let(:software_client) { instance_double(DInstaller::DBus::Clients::Software) } let(:language_client) { instance_double(DInstallerCli::Clients::Language) } let(:storage_client) { instance_double(DInstallerCli::Clients::Storage) } let(:users_client) { instance_double(DInstaller::DBus::Clients::Users) } diff --git a/cli/test/dinstaller_cli/commands/software_test.rb b/cli/test/dinstaller_cli/commands/software_test.rb index 3112c5eb07..04be780b35 100644 --- a/cli/test/dinstaller_cli/commands/software_test.rb +++ b/cli/test/dinstaller_cli/commands/software_test.rb @@ -21,17 +21,17 @@ require_relative "../../test_helper" require "dinstaller_cli/commands/software" -require "dinstaller_cli/clients/software" +require "dinstaller/dbus/clients/software" describe DInstallerCli::Commands::Software do subject { described_class.new } before do allow(subject).to receive(:say) - allow(DInstallerCli::Clients::Software).to receive(:new).and_return(client) + allow(DInstaller::DBus::Clients::Software).to receive(:new).and_return(client) end - let(:client) { instance_double(DInstallerCli::Clients::Software) } + let(:client) { instance_double(DInstaller::DBus::Clients::Software) } describe "#available_products" do before do diff --git a/service/Gemfile.lock b/service/Gemfile.lock index e2a3658287..71e0e09e83 100644 --- a/service/Gemfile.lock +++ b/service/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - d-installer (0.2) + d-installer (0.3) cfa (~> 1.0.2) cfa_grub2 (~> 2.0.0) cheetah (~> 1.0.0) @@ -51,7 +51,7 @@ GEM rspec-support (~> 3.11.0) rspec-support (3.11.0) ruby-augeas (0.5.0) - ruby-dbus (0.18.0.beta6) + ruby-dbus (0.18.0.beta7) rexml simplecov (0.21.2) docile (~> 1.1) diff --git a/service/bin/d-installer b/service/bin/d-installer index dabd14070f..0fb40e0d6b 100755 --- a/service/bin/d-installer +++ b/service/bin/d-installer @@ -30,8 +30,42 @@ require "rubygems" require "bundler/setup" require "dinstaller/dbus/service_runner" -service_name = $ARGV[0]&.to_sym || :manager -service_runner = DInstaller::DBus::ServiceRunner.new( - service_name, logger: Logger.new($stdout) -) -service_runner.run +# Runs the service with the given name +# +# @param name [Symbol] Service name +# @see ORDERED_SERVICES +def start_service(name) + logger = Logger.new($stdout, progname: name.to_s) + service_runner = DInstaller::DBus::ServiceRunner.new( + name, logger: logger + ) + service_runner.run +end + +ORDERED_SERVICES = [:software, :users, :manager].freeze + +# Starts all the services +# +# @return [Array] PIDs +# @see ORDERED_SERVICES +def start_all_services + ORDERED_SERVICES.map do |name| + fork { exec("#{__FILE__} #{name}") } + end +end + +if ARGV.empty? + pids = start_all_services + Signal.trap("SIGINT") do + puts "Stopping all services..." + exit + end + Process.wait +else + name = ARGV[0] + Signal.trap("SIGINT") do + puts "Stopping #{name} service..." + exit + end + start_service(name.to_sym) +end diff --git a/service/lib/dinstaller/config.rb b/service/lib/dinstaller/config.rb index 9e62c5d6c3..a339fbc52e 100644 --- a/service/lib/dinstaller/config.rb +++ b/service/lib/dinstaller/config.rb @@ -19,7 +19,6 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require "yast" require "yaml" require "dinstaller/config_reader" @@ -37,8 +36,8 @@ class << self attr_accessor :current, :base # Loads base and current config reading configuration from the system - def load - @base = ConfigReader.new.config + def load(logger = Logger.new($stdout)) + @base = ConfigReader.new(logger: logger).config @current = @base&.copy end diff --git a/service/lib/dinstaller/config_reader.rb b/service/lib/dinstaller/config_reader.rb index b641563450..e7ac045b36 100644 --- a/service/lib/dinstaller/config_reader.rb +++ b/service/lib/dinstaller/config_reader.rb @@ -156,8 +156,6 @@ def file_paths_in(path) elsif File.directory?(path) Dir.glob("#{path}/*.{yml,yaml}") else - logger.debug("Ignoring not valid path: #{path}") - [] end end diff --git a/service/lib/dinstaller/dbus/clients.rb b/service/lib/dinstaller/dbus/clients.rb index f21a5e760a..3eddf3bc26 100644 --- a/service/lib/dinstaller/dbus/clients.rb +++ b/service/lib/dinstaller/dbus/clients.rb @@ -30,4 +30,5 @@ module Clients end require "dinstaller/dbus/clients/dinstaller" +require "dinstaller/dbus/clients/software" require "dinstaller/dbus/clients/users" diff --git a/service/lib/dinstaller/dbus/clients/software.rb b/service/lib/dinstaller/dbus/clients/software.rb new file mode 100644 index 0000000000..32c6905be1 --- /dev/null +++ b/service/lib/dinstaller/dbus/clients/software.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +# Copyright (c) [2022] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# 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. + +require "dbus" + +module DInstaller + module DBus + module Clients + # D-Bus client for software configuration + class Software + def initialize + @dbus_object = service.object("/org/opensuse/DInstaller/Software1") + @dbus_object.introspect + end + + # Available products for the installation + # + # @return [Array>] name and display name of each product + def available_products + dbus_object["org.opensuse.DInstaller.Software1"]["AvailableBaseProducts"].map do |l| + l[0..1] + end + end + + # Product selected to install + # + # @return [String] name of the product + def selected_product + dbus_object["org.opensuse.DInstaller.Software1"]["SelectedBaseProduct"] + end + + # Selects the product to install + # + # @param name [String] + def select_product(name) + dbus_object.SelectProduct(name) + end + + # Starts the probing process + # + # If a block is given, the method returns inmmediatelly and the probing is performed in an + # asynchronous way. + # + # @param done [Proc] Block to execute once the probing is done + def probe(&done) + dbus_object.Probe(&done) + end + + # Performs the packages installation + def install + dbus_object.Install + end + + # Makes the software proposal + def propose + dbus_object.Propose + end + + # Finishes the software installation + def finish + dbus_object.Finish + end + + private + + # @return [::DBus::Object] + attr_reader :dbus_object + + # @return [::DBus::Service] + def service + @service ||= bus.service("org.opensuse.DInstaller.Software") + end + + def bus + @bus ||= ::DBus::SystemBus.instance + end + end + end + end +end diff --git a/service/lib/dinstaller/dbus/manager.rb b/service/lib/dinstaller/dbus/manager.rb index e4706f0f02..20d9ae9e79 100644 --- a/service/lib/dinstaller/dbus/manager.rb +++ b/service/lib/dinstaller/dbus/manager.rb @@ -94,15 +94,7 @@ def error_messages # @see DInstaller::Manager#progress def progress - prg = backend.progress - - [ - prg.message, - prg.total_steps, - prg.current_step, - prg.total_minor_steps, - prg.current_minor_step - ].freeze + backend.progress.to_a end private diff --git a/service/lib/dinstaller/dbus/manager_service.rb b/service/lib/dinstaller/dbus/manager_service.rb index 53dfc3dfd1..957a3619e1 100644 --- a/service/lib/dinstaller/dbus/manager_service.rb +++ b/service/lib/dinstaller/dbus/manager_service.rb @@ -23,7 +23,6 @@ require "dinstaller/manager" require "dinstaller/dbus/manager" require "dinstaller/dbus/language" -require "dinstaller/dbus/software" require "dinstaller/dbus/storage/proposal" require "dinstaller/dbus/storage/actions" require "dinstaller/dbus/questions" @@ -99,7 +98,6 @@ def dbus_objects @dbus_objects ||= [ manager_dbus, language_dbus, - software_dbus, storage_proposal_dbus, storage_actions_dbus, questions_dbus @@ -114,10 +112,6 @@ def language_dbus @language_dbus ||= DInstaller::DBus::Language.new(manager.language, logger) end - def software_dbus - @software_dbus ||= DInstaller::DBus::Software.new(manager.software, logger) - end - def storage_proposal_dbus @storage_proposal_dbus ||= DInstaller::DBus::Storage::Proposal.new( manager.storage.proposal, storage_actions_dbus, logger diff --git a/service/lib/dinstaller/dbus/service_runner.rb b/service/lib/dinstaller/dbus/service_runner.rb index 113dba9b27..b3b1d318ad 100644 --- a/service/lib/dinstaller/dbus/service_runner.rb +++ b/service/lib/dinstaller/dbus/service_runner.rb @@ -20,7 +20,7 @@ # find current contact information at www.suse.com. require "eventmachine" -require "dinstaller/manager" +require "logger" module DInstaller module DBus @@ -45,6 +45,7 @@ def initialize(name, logger: Logger.new($stdout)) # # This method listens for D-Bus calls. def run + initialize_yast service = build_service(name, logger) # TODO: implement a #start method in all services, # which is equivalent to #export in most cases. @@ -60,7 +61,9 @@ def run # Configuration def config - Config.load unless Config.current + # TODO: do not require "yast" until it is needed + require "dinstaller/config" + Config.load(logger) unless Config.current Config.current end @@ -76,6 +79,19 @@ def build_service(name, logger) rescue LoadError, NameError raise "Service '#{name}' not found" end + + # Initializes YaST + def initialize_yast + require "yast" + Yast.import "Mode" + Yast.import "Stage" + + Yast::Mode.SetUI("commandline") + Yast::Mode.SetMode("installation") + # Set stage to initial, so it will act as installer for some cases like + # proposing installer instead of reading current one + Yast::Stage.Set("initial") + end end end end diff --git a/service/lib/dinstaller/dbus/software.rb b/service/lib/dinstaller/dbus/software.rb index fc209c7b73..902c609f3d 100644 --- a/service/lib/dinstaller/dbus/software.rb +++ b/service/lib/dinstaller/dbus/software.rb @@ -20,6 +20,7 @@ # find current contact information at www.suse.com. require "dbus" +require "dinstaller/progress" module DInstaller module DBus @@ -39,9 +40,12 @@ def initialize(backend, logger) @backend = backend @logger = logger + register_progress_callback + super(PATH) end + # rubocop:disable Metrics/BlockLength dbus_interface SOFTWARE_INTERFACE do dbus_reader :available_base_products, "a(ssa{sv})" attr_writer :available_base_products @@ -64,7 +68,32 @@ def initialize(backend, logger) dbus_method :ProvisionsSelected, "in Provisions:as, out Result:ab" do |provisions| [provisions.map { |p| backend.provision_selected?(p) }] end + + dbus_method :Propose do + backend.propose + end + + dbus_method :Probe do + backend.probe + end + + dbus_method :Install do + backend.install + end + + dbus_method :Finish do + backend.finish + end + + # Progress has struct with values: + # s message + # t total major steps to do + # t current major step (0-based) + # t total minor steps. Can be zero which means no minor steps + # t current minor step + dbus_reader :progress, "(stttt)" end + # rubocop:enable Metrics/BlockLength def available_base_products backend.products.map do |product| @@ -80,6 +109,10 @@ def select_product(product_id) backend.select_product(product_id) end + def progress + backend.progress.to_a + end + private # @return [Logger] @@ -87,6 +120,15 @@ def select_product(product_id) # @return [DInstaller::Software] attr_reader :backend + + # Registers callback to be called when the progress changes + # + # The callback will emit a signal + def register_progress_callback + backend.progress.on_change do + PropertiesChanged(SOFTWARE_INTERFACE, { "Progress" => progress }, []) + end + end end end end diff --git a/service/lib/dinstaller/dbus/software_service.rb b/service/lib/dinstaller/dbus/software_service.rb new file mode 100644 index 0000000000..903bce3004 --- /dev/null +++ b/service/lib/dinstaller/dbus/software_service.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Copyright (c) [2022] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# 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. + +require "dbus" +require "dinstaller/dbus/software" +require "dinstaller/software" + +module DInstaller + module DBus + # D-Bus service (org.opensuse.DInstaller.Software) + # + # It connects to the system D-Bus and answers requests on objects below + # `/org/opensuse/DInstaller/Software`. + class SoftwareService + SERVICE_NAME = "org.opensuse.DInstaller.Software" + private_constant :SERVICE_NAME + + # D-Bus connection + # + # @return [::DBus::Connection] + attr_reader :bus + + # @param config [Config] Configuration object + # @param logger [Logger] + def initialize(config, logger = nil) + @backend = DInstaller::Software.new(config, logger) + @logger = logger || Logger.new($stdout) + @bus = ::DBus::SystemBus.instance + end + + # Exports the software object through the D-Bus service + def export + dbus_objects.each { |o| service.export(o) } + paths = dbus_objects.map(&:path).join(", ") + logger.info "Exported #{paths} objects" + end + + # Call this from some main loop to dispatch the D-Bus messages + def dispatch + bus.dispatch_message_queue + end + + private + + # @return [Logger] + attr_reader :logger, :backend + + # @return [::DBus::Service] + def service + @service ||= bus.request_service(SERVICE_NAME) + end + + # @return [Array<::DBus::Object>] + def dbus_objects + @dbus_objects ||= [ + DInstaller::DBus::Software.new(@backend, logger) + ] + end + end + end +end diff --git a/service/lib/dinstaller/manager.rb b/service/lib/dinstaller/manager.rb index 77cc040d2f..2b075f6e8e 100644 --- a/service/lib/dinstaller/manager.rb +++ b/service/lib/dinstaller/manager.rb @@ -29,9 +29,9 @@ require "dinstaller/progress" require "dinstaller/questions_manager" require "dinstaller/security" -require "dinstaller/software" require "dinstaller/status_manager" require "dinstaller/storage" +require "dinstaller/dbus/clients/software" require "dinstaller/dbus/clients/users" Yast.import "Stage" @@ -41,7 +41,8 @@ module DInstaller # # It is responsible for orchestrating the installation process. For module # specific stuff it delegates it to the corresponding module class (e.g., - # {DInstaller::Software}, {DInstaller::Storage::Proposal}, etc.). + # {DInstaller::Network}, {DInstaller::Storage::Proposal}, etc.) or asks + # other services via D-Bus (e.g., `org.opensuse.DInstaller.Software`). class Manager # @return [Logger] attr_reader :logger @@ -64,8 +65,6 @@ def initialize(config, logger) @status_manager = StatusManager.new(Status::Error.new) # temporary status until probing starts @questions_manager = QuestionsManager.new(logger) @progress = Progress.new - - initialize_yast end # Sets up the installation process @@ -102,10 +101,10 @@ def install Yast::WFM.CallFunction("inst_bootloader", []) progress.next_step("Installing Software") - software.install(progress) + software.install on_target do - progress.next_step("Writting Users") + progress.next_step("Writing Users") users.write(progress) progress.next_step("Writing Network Configuration") @@ -119,7 +118,7 @@ def install language.install(progress) progress.next_step("Writing repositories information") - software.finish(progress) + software.finish progress.next_step("Finishing installation") finish_installation @@ -134,7 +133,7 @@ def install # # @return [Software] def software - @software ||= Software.new(logger, config) + @software ||= DBus::Clients::Software.new end # Language manager @@ -176,15 +175,6 @@ def security attr_reader :config - # Initializes YaST - def initialize_yast - Yast::Mode.SetUI("commandline") - Yast::Mode.SetMode("installation") - # Set stage to initial, so it will act as installer for some cases like - # proposing installer instead of reading current one - Yast::Stage.Set("initial") - end - def setup_cockpit cockpit = CockpitManager.new(logger) cockpit.setup(config.data["web"]) @@ -205,7 +195,7 @@ def probe_steps progress.next_step("Probing Software") security.probe(progress) - software.probe(progress) + software.probe { logger.info "Probing software done" } progress.next_step("Probing Network") network.probe(progress) diff --git a/service/lib/dinstaller/package_callbacks.rb b/service/lib/dinstaller/package_callbacks.rb index c746277b8a..dc879c83bb 100644 --- a/service/lib/dinstaller/package_callbacks.rb +++ b/service/lib/dinstaller/package_callbacks.rb @@ -46,7 +46,7 @@ def setup fun_ref(method(:package_installed), "string (integer, string)") ) - @progress.init_minor_steps(@total, msg) + @progress.init_progress(@total, msg) end private @@ -58,7 +58,7 @@ def fun_ref(method, signature) # TODO: error handling def package_installed(_error, _reason) @installed += 1 - @progress.next_minor_step(msg) + @progress.next_step(msg) "" end diff --git a/service/lib/dinstaller/progress.rb b/service/lib/dinstaller/progress.rb index b1d063194b..873054b054 100644 --- a/service/lib/dinstaller/progress.rb +++ b/service/lib/dinstaller/progress.rb @@ -109,6 +109,15 @@ def next_minor_step(message) trigger_callbacks end + # Returns an array containing the progress information + # + # Useful to expose the progress information as a D-Bus property. + # + # @return [Array] + def to_a + [message, total_steps, current_step, total_minor_steps, current_minor_step].freeze + end + private def trigger_callbacks diff --git a/service/lib/dinstaller/software.rb b/service/lib/dinstaller/software.rb index 8629e8428b..b9516e580e 100644 --- a/service/lib/dinstaller/software.rb +++ b/service/lib/dinstaller/software.rb @@ -23,6 +23,7 @@ require "fileutils" require "dinstaller/package_callbacks" require "dinstaller/config" +require "dinstaller/progress" require "y2packager/product" Yast.import "PackageInstallation" @@ -42,11 +43,15 @@ class Software attr_reader :product, :products - def initialize(logger, config) + # @return [Progress] + attr_reader :progress + + def initialize(config, logger) @logger = logger @products = [] @product = "" # do not use nil here, otherwise dbus crash @config = config + @progress = Progress.new end def select_product(name) @@ -55,7 +60,7 @@ def select_product(name) @product = name end - def probe(progress) + def probe logger.info "Probing software" store_original_repos Yast::Pkg.SetSolverFlags( @@ -64,22 +69,22 @@ def probe(progress) # as we use liveDVD with normal like ENV, lets temporary switch to normal to use its repos Yast::Stage.Set("normal") - progress.init_minor_steps(3, "Initialiaze target repositories") + progress.init_progress(3, "Initialize target repositories") Yast::Pkg.TargetInitialize("/") import_gpg_keys - progress.next_minor_step("Initialize sources") + progress.next_step("Initialize sources") add_base_repo - progress.next_minor_step("Searching for supported products") + progress.next_step("Searching for supported products") @products = find_products @product = @products.first&.name || "" raise "No product available" if @product.empty? - progress.next_minor_step("Making initial proposal") + progress.next_step("Making the initial proposal") proposal = Yast::Packages.Proposal(force_reset = true, reinit = false, _simple = true) logger.info "proposal #{proposal["raw_proposal"]}" - progress.next_minor_step("Software probing finished") + progress.next_step("Software probing finished") Yast::Stage.Set("initial") end @@ -101,7 +106,7 @@ def propose nil end - def install(progress) + def install PackageCallbacks.setup(progress, count_packages) # TODO: error handling @@ -116,12 +121,12 @@ def install(progress) end # Writes the repositories information to the installed system - # - # @param _progress [Progress] Progress reporting object - def finish(_progress) + def finish + progress.init_progress(1, "Writing repositories to the target system") Yast::Pkg.SourceSaveAll Yast::Pkg.TargetFinish Yast::Pkg.SourceCacheCopyTo(Yast::Installation.destdir) + progress.next_step("Restoring original repositories") restore_original_repos end diff --git a/service/share/dbus.conf b/service/share/dbus.conf index 4473f7849b..1a09f31685 100644 --- a/service/share/dbus.conf +++ b/service/share/dbus.conf @@ -6,6 +6,7 @@ + @@ -14,10 +15,17 @@ send_interface="org.freedesktop.DBus.Introspectable"/> + + + + + + diff --git a/cli/test/dinstaller_cli/clients/software_test.rb b/service/test/dinstaller/dbus/clients/software_test.rb similarity index 79% rename from cli/test/dinstaller_cli/clients/software_test.rb rename to service/test/dinstaller/dbus/clients/software_test.rb index ce4f5842e7..4f9ef275e7 100644 --- a/cli/test/dinstaller_cli/clients/software_test.rb +++ b/service/test/dinstaller/dbus/clients/software_test.rb @@ -19,14 +19,14 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require_relative "../../test_helper" -require "dinstaller_cli/clients/software" +require_relative "../../../test_helper" +require "dinstaller/dbus/clients/software" require "dbus" -describe DInstallerCli::Clients::Software do +describe DInstaller::DBus::Clients::Software do before do allow(::DBus::SystemBus).to receive(:instance).and_return(bus) - allow(bus).to receive(:service).with("org.opensuse.DInstaller").and_return(service) + allow(bus).to receive(:service).with("org.opensuse.DInstaller.Software").and_return(service) allow(service).to receive(:object).with("/org/opensuse/DInstaller/Software1") .and_return(dbus_object) allow(dbus_object).to receive(:introspect) @@ -79,4 +79,24 @@ subject.select_product("Tumbleweed") end end + + describe "#Probe" do + let(:dbus_object) { double(::DBus::ProxyObject, Probe: nil) } + it "calls the D-Bus Probe method" do + expect(dbus_object).to receive(:Probe) + + subject.probe + end + + context "when a block is given" do + it "passes the block to the Probe method (async)" do + callback = proc {} + expect(dbus_object).to receive(:Probe) do |&block| + expect(block).to be(callback) + end + + subject.probe(&callback) + end + end + end end diff --git a/service/test/dinstaller/dbus/manager_service_test.rb b/service/test/dinstaller/dbus/manager_service_test.rb index 80836ac7c2..abe807c224 100644 --- a/service/test/dinstaller/dbus/manager_service_test.rb +++ b/service/test/dinstaller/dbus/manager_service_test.rb @@ -50,15 +50,6 @@ service.export end - it "exports the software manager object" do - software_obj = instance_double(DInstaller::DBus::Software, path: nil) - allow(DInstaller::DBus::Software).to receive(:new) - .with(manager.software, logger).and_return(software_obj) - - expect(bus_service).to receive(:export).with(software_obj) - service.export - end - it "exports the storage actions object" do actions_obj = instance_double(DInstaller::DBus::Storage::Actions, path: nil) allow(DInstaller::DBus::Storage::Actions).to receive(:new) diff --git a/service/test/dinstaller/manager_test.rb b/service/test/dinstaller/manager_test.rb index b3a7eac381..e0990ba63a 100644 --- a/service/test/dinstaller/manager_test.rb +++ b/service/test/dinstaller/manager_test.rb @@ -31,7 +31,9 @@ let(:cockpit) { instance_double(DInstaller::CockpitManager, setup: nil) } let(:software) do - instance_double(DInstaller::Software, probe: nil, install: nil, propose: nil, finish: nil) + instance_double( + DInstaller::DBus::Clients::Software, probe: nil, install: nil, propose: nil, finish: nil + ) end let(:language) { instance_double(DInstaller::Language, probe: nil, install: nil) } let(:network) { instance_double(DInstaller::Network, probe: nil, install: nil) } @@ -46,7 +48,7 @@ allow(DInstaller::Language).to receive(:new).and_return(language) allow(DInstaller::Network).to receive(:new).and_return(network) allow(DInstaller::Security).to receive(:new).and_return(security) - allow(DInstaller::Software).to receive(:new).and_return(software) + allow(DInstaller::DBus::Clients::Software).to receive(:new).and_return(software) allow(DInstaller::StatusManager).to receive(:new).and_return(status_manager) allow(DInstaller::Storage::Manager).to receive(:new).and_return(storage) allow_any_instance_of(DInstaller::DBus::Clients::Users).to receive(:write) @@ -62,7 +64,7 @@ end it "calls #probe method of each module passing a progress object" do - expect(software).to receive(:probe).with(subject.progress) + expect(software).to receive(:probe) expect(security).to receive(:probe).with(subject.progress) expect(language).to receive(:probe).with(subject.progress) expect(network).to receive(:probe).with(subject.progress) @@ -88,7 +90,7 @@ it "calls #install (or #write) method of each module passing a progress object" do expect(language).to receive(:install).with(subject.progress) expect(network).to receive(:install).with(subject.progress) - expect(software).to receive(:install).with(subject.progress) + expect(software).to receive(:install) expect(security).to receive(:write).with(subject.progress) expect(storage).to receive(:install).with(subject.progress) expect(users_client).to receive(:write).with(subject.progress) diff --git a/service/test/dinstaller/software_test.rb b/service/test/dinstaller/software_test.rb index 496118d54f..61c8f82983 100644 --- a/service/test/dinstaller/software_test.rb +++ b/service/test/dinstaller/software_test.rb @@ -25,7 +25,7 @@ require "dinstaller/progress" describe DInstaller::Software do - subject { described_class.new(logger, config) } + subject { described_class.new(config, logger) } let(:logger) { Logger.new($stdout) } let(:config) { DInstaller::Config.new } @@ -55,7 +55,7 @@ describe "#probe" do it "initializes the package system" do expect(Yast::Pkg).to receive(:TargetInitialize).with("/") - subject.probe(progress) + subject.probe end context "when GPG keys are available at /" do @@ -65,13 +65,13 @@ it "imports the GPG keys" do expect(Yast::Pkg).to receive(:ImportGPGKey).with(gpg_keys.first, true) - subject.probe(progress) + subject.probe end end it "creates a packages proposal" do expect(Yast::Packages).to receive(:Proposal) - subject.probe(progress) + subject.probe end it "registers the repository from config" do @@ -81,14 +81,14 @@ ) expect(Yast::Pkg).to receive(:SourceCreate).with(url, "/") expect(Yast::Pkg).to receive(:SourceSaveAll) - subject.probe(progress) + subject.probe end context "when no supported products are found" do let(:products) { [] } it "raises an exception" do - expect { subject.probe(progress) }.to raise_error(RuntimeError) + expect { subject.probe }.to raise_error(RuntimeError) end end end @@ -101,7 +101,7 @@ end describe "after probing" do - before { subject.probe(progress) } + before { subject.probe } it "returns the name of the found products that are supported" do expect(subject.products).to eq([tw_prod]) diff --git a/web/src/client/index.js b/web/src/client/index.js index bdb79cc5eb..cbbe8ed6dd 100644 --- a/web/src/client/index.js +++ b/web/src/client/index.js @@ -31,7 +31,6 @@ import QuestionsClient from "./questions"; import cockpit from "../lib/cockpit"; const SERVICE_NAME = "org.opensuse.DInstaller"; -const USERS_SERVICE_NAME = "org.opensuse.DInstaller.Users"; const createClient = () => { const client = cockpit.dbus(SERVICE_NAME, { @@ -39,18 +38,13 @@ const createClient = () => { superuser: "try" }); - const usersClient = cockpit.dbus(USERS_SERVICE_NAME, { - bus: "system", - superuser: "try" - }); - return { language: new LanguageClient(client), manager: new ManagerClient(client), monitor: new Monitor(SERVICE_NAME), - software: new SoftwareClient(client), + software: new SoftwareClient(), storage: new StorageClient(client), - users: new UsersClient(usersClient), + users: new UsersClient(), questions: new QuestionsClient(client) }; }; diff --git a/web/src/client/software.js b/web/src/client/software.js index 827f90adda..a95a73b8d9 100644 --- a/web/src/client/software.js +++ b/web/src/client/software.js @@ -20,7 +20,9 @@ */ import { applyMixin, withDBus } from "./mixins"; +import cockpit from "../lib/cockpit"; +const SOFTWARE_SERVICE = "org.opensuse.DInstaller.Software"; const SOFTWARE_IFACE = "org.opensuse.DInstaller.Software1"; const SOFTWARE_PATH = "/org/opensuse/DInstaller/Software1"; @@ -28,8 +30,10 @@ const SOFTWARE_PATH = "/org/opensuse/DInstaller/Software1"; * Software client */ class SoftwareClient { - constructor(dbusClient) { - this._client = dbusClient; + constructor() { + this._client = cockpit.dbus(SOFTWARE_SERVICE, { + bus: "system", superuser: "try" + }); } /** diff --git a/web/src/client/software.test.js b/web/src/client/software.test.js index 764483db12..7aae682730 100644 --- a/web/src/client/software.test.js +++ b/web/src/client/software.test.js @@ -20,6 +20,7 @@ */ import SoftwareClient from "./software"; +import cockpit from "../lib/cockpit"; const SOFTWARE_IFACE = "org.opensuse.DInstaller.Software1"; @@ -33,6 +34,7 @@ const softProxy = { }; beforeEach(() => { + cockpit.dbus = jest.fn().mockImplementation(() => dbusClient); dbusClient.proxy = jest.fn().mockImplementation(iface => { if (iface === SOFTWARE_IFACE) return softProxy; }); diff --git a/web/src/client/users.js b/web/src/client/users.js index 4ad38df71e..b878e851e9 100644 --- a/web/src/client/users.js +++ b/web/src/client/users.js @@ -21,7 +21,9 @@ const USERS_IFACE = "org.opensuse.DInstaller.Users1"; */ import { applyMixin, withDBus } from "./mixins"; +import cockpit from "../lib/cockpit"; +const USERS_SERVICE = "org.opensuse.DInstaller.Users"; const USERS_IFACE = "org.opensuse.DInstaller.Users1"; const USERS_PATH = "/org/opensuse/DInstaller/Users1"; @@ -29,8 +31,10 @@ const USERS_PATH = "/org/opensuse/DInstaller/Users1"; * Users client */ class UsersClient { - constructor(dbusClient) { - this._client = dbusClient; + constructor() { + this._client = cockpit.dbus(USERS_SERVICE, { + bus: "system", superuser: "try" + }); } /** diff --git a/web/src/client/users.test.js b/web/src/client/users.test.js index 5e0625dd80..a9bf8ab3b0 100644 --- a/web/src/client/users.test.js +++ b/web/src/client/users.test.js @@ -20,6 +20,7 @@ */ import UsersClient from "./users"; +import cockpit from "../lib/cockpit"; const USERS_IFACE = "org.opensuse.DInstaller.Users1"; @@ -38,6 +39,7 @@ const usersProxy = { }; beforeEach(() => { + cockpit.dbus = jest.fn().mockImplementation(() => dbusClient); dbusClient.proxy = jest.fn().mockImplementation(iface => { if (iface === USERS_IFACE) return usersProxy; });