From 78b4ab41133629458f99829b3f1865b84d7e9518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:05:48 +0100 Subject: [PATCH 1/7] [service] Add Issue class --- service/lib/agama/issue.rb | 77 ++++++++++++++++++++++++++++++++ service/test/agama/issue_test.rb | 45 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 service/lib/agama/issue.rb create mode 100644 service/test/agama/issue_test.rb diff --git a/service/lib/agama/issue.rb b/service/lib/agama/issue.rb new file mode 100644 index 0000000000..067cce1a7c --- /dev/null +++ b/service/lib/agama/issue.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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. + +module Agama + # Represents an issue + class Issue + # Description of the issue + # + # @return [String] + attr_reader :description + + # Details of the isssue, if any + # + # @return [String, nil] + attr_reader :details + + # Source of the issue, see {Source} + # + # @return [Symbol, nil] + attr_reader :source + + # Severity of the issue, see {Severity} + # + # @return [Symbol] + attr_reader :severity + + # Defines possible sources + module Source + SYSTEM = :system + CONFIG = :config + end + + # Defines different severities + module Severity + WARN = :warn + ERROR = :error + end + + # Constructor + # + # @param description [String] + # @param details [String, nil] + # @param source [symbol, nil] + # @param severity [symbol] + def initialize(description, details: "", source: nil, severity: Severity::WARN) + @description = description + @details = details + @source = source + @severity = severity + end + + # Whether the issue has error severity + # + # @return [Boolean] + def error? + severity == Severity::ERROR + end + end +end diff --git a/service/test/agama/issue_test.rb b/service/test/agama/issue_test.rb new file mode 100644 index 0000000000..ae2a00d858 --- /dev/null +++ b/service/test/agama/issue_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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_relative "../test_helper" +require "agama/issue" + +describe Agama::Issue do + subject { described_class.new("Issue test", severity: severity) } + + describe "#error?" do + context "if the issue has warn severity" do + let(:severity) { Agama::Issue::Severity::WARN } + + it "returns false" do + expect(subject.error?).to eq(false) + end + end + + context "if the issue has error severity" do + let(:severity) { Agama::Issue::Severity::ERROR } + + it "returns true" do + expect(subject.error?).to eq(true) + end + end + end +end From 04e87fdeeb54b7ce88e5daf9bbc513b9c1518933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:08:28 +0100 Subject: [PATCH 2/7] [service] Add issues interface --- service/lib/agama/dbus.rb | 24 +++++- service/lib/agama/dbus/interfaces.rb | 33 ++++++++ service/lib/agama/dbus/interfaces/issues.rb | 74 ++++++++++++++++++ .../test/agama/dbus/interfaces/issues_test.rb | 76 +++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 service/lib/agama/dbus/interfaces.rb create mode 100644 service/lib/agama/dbus/interfaces/issues.rb create mode 100644 service/test/agama/dbus/interfaces/issues_test.rb diff --git a/service/lib/agama/dbus.rb b/service/lib/agama/dbus.rb index 5c9f5f3491..45354f6c27 100644 --- a/service/lib/agama/dbus.rb +++ b/service/lib/agama/dbus.rb @@ -1,7 +1,26 @@ # 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. + module Agama - # Namespace for DBus API + # Namespace for D-Bus API module DBus end end @@ -9,3 +28,6 @@ module DBus require "agama/dbus/manager" require "agama/dbus/language" require "agama/dbus/software" +require "agama/dbus/storage" +require "agama/dbus/users" +require "agama/dbus/questions" diff --git a/service/lib/agama/dbus/interfaces.rb b/service/lib/agama/dbus/interfaces.rb new file mode 100644 index 0000000000..a439d3f505 --- /dev/null +++ b/service/lib/agama/dbus/interfaces.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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. + +module Agama + module DBus + # Namespace for generic D-Bus interfaces + module Interfaces + end + end +end + +require "agama/dbus/interfaces/issues" +require "agama/dbus/interfaces/progress" +require "agama/dbus/interfaces/service_status" +require "agama/dbus/interfaces/validation" diff --git a/service/lib/agama/dbus/interfaces/issues.rb b/service/lib/agama/dbus/interfaces/issues.rb new file mode 100644 index 0000000000..758edba54f --- /dev/null +++ b/service/lib/agama/dbus/interfaces/issues.rb @@ -0,0 +1,74 @@ +# 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 "agama/issue" + +module Agama + module DBus + module Interfaces + # Mixin to define the Issues D-Bus interface + # + # @note This mixin is expected to be included in a class that inherits from {DBus::BaseObject} + # and it requires a #issues method that returns an array of {Issue} objects. + module Issues + ISSUES_INTERFACE = "org.opensuse.Agama1.Issues" + + # Issues with the D-Bus format + # + # @return [Array] The description, details, source + # and severity of each issue. + # Source: 1 for system, 2 for config and 3 for unknown. + # Severity: 0 for warn and 1 for error. + def dbus_issues + issues.map do |issue| + source = case issue.source + when Agama::Issue::Source::SYSTEM + 1 + when Agama::Issue::Source::CONFIG + 2 + else + 0 + end + severity = issue.severity == Agama::Issue::Severity::WARN ? 0 : 1 + + [issue.description, issue.details.to_s, source, severity] + end + end + + # Emits the signal for properties changed + def issues_properties_changed + dbus_properties_changed(ISSUES_INTERFACE, + interfaces_and_properties[ISSUES_INTERFACE], []) + end + + def self.included(base) + base.class_eval do + dbus_interface ISSUES_INTERFACE do + # @see {#dbus_issues} + dbus_reader :dbus_issues, "a(ssuu)", dbus_name: "All" + end + end + end + end + end + end +end diff --git a/service/test/agama/dbus/interfaces/issues_test.rb b/service/test/agama/dbus/interfaces/issues_test.rb new file mode 100644 index 0000000000..146e579d6d --- /dev/null +++ b/service/test/agama/dbus/interfaces/issues_test.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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_relative "../../../test_helper" +require "dbus" +require "agama/dbus/interfaces/issues" +require "agama/issue" + +class DBusObjectWithIssuesInterface < ::DBus::Object + include Agama::DBus::Interfaces::Issues + + def initialize + super("org.opensuse.Agama.UnitTests") + end + + def issues + [ + Agama::Issue.new("Issue 1", + details: "Details 1", + source: Agama::Issue::Source::SYSTEM, + severity: Agama::Issue::Severity::WARN), + Agama::Issue.new("Issue 2", + details: "Details 2", + source: Agama::Issue::Source::CONFIG, + severity: Agama::Issue::Severity::ERROR) + ] + end +end + +describe DBusObjectWithIssuesInterface do + let(:issues_interface) do + Agama::DBus::Interfaces::Issues::ISSUES_INTERFACE + end + + it "defines Issues D-Bus interface" do + expect(subject.intfs.keys).to include(issues_interface) + end + + describe "#dbus_issues" do + it "returns the info of all issues" do + result = subject.dbus_issues + + expect(result).to contain_exactly( + ["Issue 1", "Details 1", 1, 0], + ["Issue 2", "Details 2", 2, 1] + ) + end + end + + describe "#issues_properties_changed" do + it "emits a properties changed signal for issues" do + expect(subject).to receive(:dbus_properties_changed) + .with(issues_interface, anything, anything) + + subject.issues_properties_changed + end + end +end From f957f2e0193a6c95d420ca34286ccd5334a82f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:43:24 +0100 Subject: [PATCH 3/7] [service] Add shared examples for progress --- service/test/agama/manager_test.rb | 3 +++ service/test/agama/software/manager_test.rb | 3 +++ service/test/agama/storage/finisher_test.rb | 3 +++ ...ress_test.rb => with_progress_examples.rb} | 21 +++++++------------ 4 files changed, 17 insertions(+), 13 deletions(-) rename service/test/agama/{with_progress_test.rb => with_progress_examples.rb} (86%) diff --git a/service/test/agama/manager_test.rb b/service/test/agama/manager_test.rb index 69cf6f555f..24d00a9138 100644 --- a/service/test/agama/manager_test.rb +++ b/service/test/agama/manager_test.rb @@ -20,6 +20,7 @@ # find current contact information at www.suse.com. require_relative "../test_helper" +require_relative "./with_progress_examples" require "agama/manager" require "agama/config" require "agama/question" @@ -213,4 +214,6 @@ expect(path).to eq("/tmp/y2log-hWBn95.tar.xz") end end + + include_examples "progress" end diff --git a/service/test/agama/software/manager_test.rb b/service/test/agama/software/manager_test.rb index e706943c71..1dd557a6f1 100644 --- a/service/test/agama/software/manager_test.rb +++ b/service/test/agama/software/manager_test.rb @@ -20,6 +20,7 @@ # find current contact information at www.suse.com. require_relative "../../test_helper" +require_relative "../with_progress_examples" require_relative File.join( SRC_PATH, "agama", "dbus", "y2dir", "software", "modules", "PackageCallbacks.rb" ) @@ -282,4 +283,6 @@ end end end + + include_examples "progress" end diff --git a/service/test/agama/storage/finisher_test.rb b/service/test/agama/storage/finisher_test.rb index c4d325a783..3f5f3eac19 100644 --- a/service/test/agama/storage/finisher_test.rb +++ b/service/test/agama/storage/finisher_test.rb @@ -21,6 +21,7 @@ require_relative "../../test_helper" require_relative "storage_helpers" +require_relative "../with_progress_examples" require "agama/helpers" require "agama/config" require "agama/security" @@ -137,4 +138,6 @@ subject.run end end + + include_examples "progress" end diff --git a/service/test/agama/with_progress_test.rb b/service/test/agama/with_progress_examples.rb similarity index 86% rename from service/test/agama/with_progress_test.rb rename to service/test/agama/with_progress_examples.rb index 1c086b3ecd..a4e463e799 100644 --- a/service/test/agama/with_progress_test.rb +++ b/service/test/agama/with_progress_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2023] SUSE LLC # # All Rights Reserved. # @@ -20,15 +20,8 @@ # find current contact information at www.suse.com. require_relative "../test_helper" -require "agama/with_progress" - -class WithProgressTest - include Agama::WithProgress -end - -describe WithProgressTest do - let(:logger) { Logger.new($stdout, level: :warn) } +shared_examples "progress" do describe "#progress" do context "if not progress was started" do it "returns nil" do @@ -73,18 +66,20 @@ class WithProgressTest end it "configures the 'on_change' callbacks for the new progress" do - subject.on_progress_change { logger.info("progress changes") } + callback = proc {} + subject.on_progress_change(&callback) - expect(logger).to receive(:info).with(/progress changes/) + expect(callback).to receive(:call) subject.start_progress(1) subject.progress.step("step 1") end it "configures the 'on_finish' callbacks for the new progress" do - subject.on_progress_finish { logger.info("progress finishes") } + callback = proc {} + subject.on_progress_finish(&callback) - expect(logger).to receive(:info).with(/progress finishes/) + expect(callback).to receive(:call) subject.start_progress(1) subject.progress.finish From d57181cf6e16e7eba234f7617c642d0c6329b866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:45:16 +0100 Subject: [PATCH 4/7] [service] Add issues to storage --- service/lib/agama/dbus/storage/manager.rb | 35 ++-- service/lib/agama/manager.rb | 2 +- service/lib/agama/storage/manager.rb | 122 +++++++++--- service/lib/agama/storage/proposal.rb | 69 ++++--- service/lib/agama/with_issues.rb | 48 +++++ .../test/agama/dbus/storage/manager_test.rb | 14 +- service/test/agama/manager_test.rb | 6 +- service/test/agama/storage/manager_test.rb | 185 ++++++++++++++---- service/test/agama/storage/proposal_test.rb | 65 +++--- service/test/agama/with_issues_examples.rb | 49 +++++ 10 files changed, 441 insertions(+), 154 deletions(-) create mode 100644 service/lib/agama/with_issues.rb create mode 100644 service/test/agama/with_issues_examples.rb diff --git a/service/lib/agama/dbus/storage/manager.rb b/service/lib/agama/dbus/storage/manager.rb index d7c8ccf11d..213c0f4b77 100644 --- a/service/lib/agama/dbus/storage/manager.rb +++ b/service/lib/agama/dbus/storage/manager.rb @@ -23,9 +23,9 @@ require "yast" require "agama/dbus/base_object" require "agama/dbus/with_service_status" +require "agama/dbus/interfaces/issues" require "agama/dbus/interfaces/progress" require "agama/dbus/interfaces/service_status" -require "agama/dbus/interfaces/validation" require "agama/dbus/storage/dasd_manager_interface" require "agama/dbus/storage/proposal" require "agama/dbus/storage/proposal_settings_converter" @@ -43,9 +43,9 @@ class Manager < BaseObject include WithISCSIAuth include WithServiceStatus include ::DBus::ObjectManager + include DBus::Interfaces::Issues include DBus::Interfaces::Progress include DBus::Interfaces::ServiceStatus - include DBus::Interfaces::Validation PATH = "/org/opensuse/Agama/Storage1" private_constant :PATH @@ -57,6 +57,7 @@ class Manager < BaseObject def initialize(backend, logger) super(PATH, logger: logger) @backend = backend + register_storage_callbacks register_proposal_callbacks register_progress_callbacks register_service_status_callbacks @@ -68,14 +69,18 @@ def initialize(backend, logger) register_and_extend_dasd_callbacks end + # List of issues, see {DBus::Interfaces::Issues} + # + # @return [Array] + def issues + backend.issues + end + STORAGE_INTERFACE = "org.opensuse.Agama.Storage1" private_constant :STORAGE_INTERFACE def probe - busy_while do - backend.probe - storage_properties_changed - end + busy_while { backend.probe } end def install @@ -90,7 +95,7 @@ def finish # # @return [Boolean] def deprecated_system - backend.deprecated_system + backend.deprecated_system? end dbus_interface STORAGE_INTERFACE do @@ -241,11 +246,15 @@ def proposal backend.proposal end + def register_storage_callbacks + backend.on_issues_change { issues_properties_changed } + backend.on_deprecated_system_change { storage_properties_changed } + end + def register_proposal_callbacks proposal.on_calculate do export_proposal proposal_properties_changed - update_validation end end @@ -278,12 +287,6 @@ def register_and_extend_dasd_callbacks end end - def deprecate_system - backend.deprecated_system = true - storage_properties_changed - update_validation - end - def storage_properties_changed properties = interfaces_and_properties[STORAGE_INTERFACE] dbus_properties_changed(STORAGE_INTERFACE, properties, []) @@ -294,6 +297,10 @@ def proposal_properties_changed dbus_properties_changed(PROPOSAL_CALCULATOR_INTERFACE, properties, []) end + def deprecate_system + backend.deprecated_system = true + end + def export_proposal @service.unexport(dbus_proposal) if dbus_proposal @dbus_proposal = DBus::Storage::Proposal.new(proposal, logger) diff --git a/service/lib/agama/manager.rb b/service/lib/agama/manager.rb index bbf233a7f6..e98fe2f0b3 100644 --- a/service/lib/agama/manager.rb +++ b/service/lib/agama/manager.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2022-2023] SUSE LLC # # All Rights Reserved. # diff --git a/service/lib/agama/storage/manager.rb b/service/lib/agama/storage/manager.rb index 0864863059..32bd2d5bcc 100644 --- a/service/lib/agama/storage/manager.rb +++ b/service/lib/agama/storage/manager.rb @@ -27,9 +27,10 @@ require "agama/storage/callbacks" require "agama/storage/iscsi/manager" require "agama/storage/finisher" +require "agama/issue" +require "agama/with_issues" require "agama/with_progress" require "agama/security" -require "agama/validation_error" require "agama/dbus/clients/questions" require "agama/dbus/clients/software" @@ -39,31 +40,62 @@ module Agama module Storage # Manager to handle storage configuration class Manager + include WithIssues include WithProgress + # Constructor + # + # @param config [Config] + # @param logger [Logger] + def initialize(config, logger) + @config = config + @logger = logger + register_proposal_callbacks + end + # Whether the system is in a deprecated status # # The system is usually set as deprecated as effect of managing some kind of devices, for # example, when iSCSI sessions are created. # # A deprecated system means that the probed system could not match with the current system. - attr_accessor :deprecated_system + # + # @return [Boolean] + def deprecated_system? + !!@deprecated_system + end - def initialize(config, logger) - @config = config - @logger = logger - @deprecated_system = false + # Sets whether the system is deprecated + # + # If the deprecated status changes, then callbacks are executed and the issues are + # recalculated, see {#on_deprecated_system_change}. + # + # @param value [Boolean] + def deprecated_system=(value) + return if deprecated_system? == value + + @deprecated_system = value + @on_deprecated_system_change_callbacks&.each(&:call) + update_issues + end + + # Registers a callback to be called when the system is set as deprecated + # + # @param block [Proc] + def on_deprecated_system_change(&block) + @on_deprecated_system_change_callbacks ||= [] + @on_deprecated_system_change_callbacks << block end # Probes storage devices and performs an initial proposal def probe - @deprecated_system = false start_progress(4) config.pick_product(software.selected_product) progress.step("Activating storage devices") { activate_devices } progress.step("Probing storage devices") { probe_devices } progress.step("Calculating the storage proposal") { calculate_proposal } progress.step("Selecting Linux Security Modules") { security.probe } + update_issues end # Prepares the partitioning to install the system @@ -104,14 +136,6 @@ def iscsi @iscsi ||= ISCSI::Manager.new(logger: logger) end - # Validates the storage configuration - # - # @return [Array] List of validation errors - def validate - errors = [deprecated_system_error] + proposal.validate - errors.compact - end - # Returns the client to ask the software service # # @return [Agama::DBus::Clients::Software] @@ -130,6 +154,11 @@ def software # @return [Config] attr_reader :config + # Issues are updated when the proposal is calculated + def register_proposal_callbacks + proposal.on_calculate { update_issues } + end + # Activates the devices, calling activation callbacks if needed def activate_devices callbacks = Callbacks::Activate.new(questions_client, logger) @@ -140,9 +169,13 @@ def activate_devices # Probes the devices def probe_devices + callbacks = Y2Storage::Callbacks::UserProbe.new + iscsi.probe - # TODO: better probe callbacks that can report issue to user - Y2Storage::StorageManager.instance.probe(Y2Storage::Callbacks::UserProbe.new) + Y2Storage::StorageManager.instance.probe(callbacks) + + # The system is not deprecated anymore + self.deprecated_system = false end # Calculates the proposal @@ -171,13 +204,58 @@ def add_packages Yast::PackagesProposal.SetResolvables(PROPOSAL_ID, :package, packages) end - # Returns an error if the system is deprecated + # Recalculates the list of issues + def update_issues + self.issues = system_issues + proposal.issues + end + + # Issues from the system + # + # @return [Array] + def system_issues + issues = probing_issues + [ + deprecated_system_issue, + available_devices_issue + ] + + issues.compact + end + + # Issues from the probing phase + # + # @return [Array] + def probing_issues + y2storage_issues = Y2Storage::StorageManager.instance.raw_probed.probing_issues + + y2storage_issues.map do |y2storage_issue| + details = [y2storage_issue.description, y2storage_issue.details].compact.join("\n") + Issue.new(y2storage_issue.message, + details: details, + source: Issue::Source::SYSTEM, + severity: Issue::Severity::WARN) + end + end + + # Returns an issue if the system is deprecated + # + # @return [Issue, nil] + def deprecated_system_issue + return unless deprecated_system? + + Issue.new("The system devices have changed", + source: Issue::Source::SYSTEM, + severity: Issue::Severity::ERROR) + end + + # Returns an issue if there is no available device for installation # - # @return [ValidationError, nil] - def deprecated_system_error - return unless deprecated_system + # @return [Issue, nil] + def available_devices_issue + return if proposal.available_devices.any? - ValidationError.new("The system devices have changed") + Issue.new("There is no suitable device for installation", + source: Issue::Source::SYSTEM, + severity: Issue::Severity::ERROR) end # Security manager diff --git a/service/lib/agama/storage/proposal.rb b/service/lib/agama/storage/proposal.rb index ab65ffb626..ca18fa3dc8 100644 --- a/service/lib/agama/storage/proposal.rb +++ b/service/lib/agama/storage/proposal.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2022-2023] SUSE LLC # # All Rights Reserved. # @@ -21,7 +21,7 @@ require "y2storage" require "y2storage/dialogs/guided_setup/helpers/disk" -require "agama/validation_error" +require "agama/issue" require "agama/storage/actions" require "agama/storage/proposal_settings" require "agama/storage/proposal_settings_converter" @@ -56,10 +56,22 @@ def initialize(logger, config) @on_calculate_callbacks = [] end + # Resets the current proposal def reset @settings = nil end + # List of issues + # + # @return [Array] + def issues + [ + empty_candidate_devices_issue, + missing_candidate_devices_issue, + proposal_issue + ].compact + end + # Stores callbacks to be call after calculating a proposal def on_calculate(&block) @on_calculate_callbacks << block @@ -76,6 +88,8 @@ def available_devices # # @return [Array] def candidate_devices + return [] unless proposal + proposal.settings.candidate_devices end @@ -147,20 +161,6 @@ def actions Actions.new(logger, proposal.devices.actiongraph).all end - # Validates the storage proposal - # - # @return [Array] List of validation errors - def validate - return [] if proposal.nil? - - [ - empty_available_devices_error, - empty_candidate_devices_error, - missing_candidate_devices_error, - proposal_error - ].compact - end - private # @return [Logger] @@ -258,30 +258,39 @@ def storage_manager Y2Storage::StorageManager.instance end - def empty_available_devices_error - return if available_devices.any? - - ValidationError.new("There is no suitable device for installation") - end - - def empty_candidate_devices_error - return if candidate_devices.any? + # Returns an issue if there is no candidate device + # + # @return [Issue, nil] + def empty_candidate_devices_issue + return if !proposal || candidate_devices.any? - ValidationError.new("No devices are selected for installation") + Issue.new("No devices are selected for installation", + source: Issue::Source::CONFIG, + severity: Issue::Severity::ERROR) end - def missing_candidate_devices_error + # Returns an issue if any of the candidate devices is not found + # + # @return [Issue, nil] + def missing_candidate_devices_issue available_names = available_devices.map(&:name) missing = candidate_devices - available_names return if missing.none? - ValidationError.new("Some selected devices are not found in the system") + Issue.new("Some selected devices are not found in the system", + source: Issue::Source::CONFIG, + severity: Issue::Severity::ERROR) end - def proposal_error - return unless proposal.failed? + # Returns an issue if the proposal is not valid + # + # @return [Issue, nil] + def proposal_issue + return unless proposal&.failed? - ValidationError.new("Cannot accommodate the required file systems for installation") + Issue.new("Cannot accommodate the required file systems for installation", + source: Issue::Source::CONFIG, + severity: Issue::Severity::ERROR) end # Adjusts the encryption-related settings of the given Y2Storage::ProposalSettings object diff --git a/service/lib/agama/with_issues.rb b/service/lib/agama/with_issues.rb new file mode 100644 index 0000000000..cdec10aa22 --- /dev/null +++ b/service/lib/agama/with_issues.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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. + +module Agama + # Mixin for managing issues + module WithIssues + # The list of current issues + # + # @return [Array] + def issues + @issues || [] + end + + # Sets the list of current issues + # + # @param issues [Array] + def issues=(issues) + @issues = issues + @on_issues_change_callbacks&.each(&:call) + end + + # Registers a callback to be called when the list of issues changes + # + # @param block [Proc] + def on_issues_change(&block) + @on_issues_change_callbacks ||= [] + @on_issues_change_callbacks << block + end + end +end diff --git a/service/test/agama/dbus/storage/manager_test.rb b/service/test/agama/dbus/storage/manager_test.rb index fe9a1f7373..599e76d2ab 100644 --- a/service/test/agama/dbus/storage/manager_test.rb +++ b/service/test/agama/dbus/storage/manager_test.rb @@ -40,11 +40,13 @@ let(:backend) do instance_double(Agama::Storage::Manager, - proposal: proposal, - iscsi: iscsi, - software: software, - on_progress_change: nil, - on_progress_finish: nil) + proposal: proposal, + iscsi: iscsi, + software: software, + on_progress_change: nil, + on_progress_finish: nil, + on_issues_change: nil, + on_deprecated_system_change: nil) end let(:proposal) do @@ -68,7 +70,7 @@ describe "#deprecated_system" do before do - allow(backend).to receive(:deprecated_system).and_return(deprecated) + allow(backend).to receive(:deprecated_system?).and_return(deprecated) end context "if the system is set as deprecated" do diff --git a/service/test/agama/manager_test.rb b/service/test/agama/manager_test.rb index 24d00a9138..96d78aeada 100644 --- a/service/test/agama/manager_test.rb +++ b/service/test/agama/manager_test.rb @@ -53,7 +53,7 @@ let(:storage) do instance_double( Agama::DBus::Clients::Storage, probe: nil, install: nil, finish: nil, - on_service_status_change: nil, valid?: true + on_service_status_change: nil, errors?: false ) end @@ -181,9 +181,9 @@ end end - context "when the storage configuration is not valid" do + context "when there are storage errors" do before do - allow(storage).to receive(:valid?).and_return(false) + allow(storage).to receive(:errors?).and_return(true) end it "returns false" do diff --git a/service/test/agama/storage/manager_test.rb b/service/test/agama/storage/manager_test.rb index 356de899b5..df03c4f3d7 100644 --- a/service/test/agama/storage/manager_test.rb +++ b/service/test/agama/storage/manager_test.rb @@ -20,12 +20,16 @@ # find current contact information at www.suse.com. require_relative "../../test_helper" +require_relative "../with_progress_examples" +require_relative "../with_issues_examples" require_relative "storage_helpers" require "agama/storage/manager" require "agama/storage/proposal_settings" require "agama/storage/iscsi/manager" require "agama/config" +require "agama/issue" require "agama/dbus/clients/questions" +require "y2storage/issue" describe Agama::Storage::Manager do include Agama::RSpec::StorageHelpers @@ -55,29 +59,99 @@ let(:bootloader_finish) { instance_double(Bootloader::FinishClient, write: nil) } let(:security) { instance_double(Agama::Security, probe: nil, write: nil) } - describe "#probe" do + describe "#deprecated_system=" do before do allow(Y2Storage::StorageManager).to receive(:instance).and_return(y2storage_manager) allow(Agama::Storage::Proposal).to receive(:new).and_return(proposal) - allow(Agama::Storage::ISCSI::Manager).to receive(:new).and_return(iscsi) + + allow(y2storage_manager).to receive(:raw_probed).and_return(raw_devicegraph) + + allow(proposal).to receive(:issues).and_return([]) + allow(proposal).to receive(:available_devices).and_return([]) end - let(:proposal) do - instance_double(Agama::Storage::Proposal, - settings: settings, - calculate: nil, - available_devices: devices) + let(:raw_devicegraph) { instance_double(Y2Storage::Devicegraph, probing_issues: []) } + + let(:proposal) { Agama::Storage::Proposal.new(logger, config) } + + let(:callback) { proc {} } + + context "if the current value is changed" do + before do + storage.deprecated_system = true + end + + it "executes the on_deprecated_system_change callbacks" do + storage.on_deprecated_system_change(&callback) + + expect(callback).to receive(:call) + + storage.deprecated_system = false + end end - let(:devices) { [disk1, disk2] } - let(:settings) { nil } + context "if the current value is not changed" do + before do + storage.deprecated_system = true + end - let(:disk1) { instance_double(Y2Storage::Disk, name: "/dev/vda") } - let(:disk2) { instance_double(Y2Storage::Disk, name: "/dev/vdb") } + it "does not execute the on_deprecated_system_change callbacks" do + storage.on_deprecated_system_change(&callback) - let(:iscsi) { Agama::Storage::ISCSI::Manager.new } + expect(callback).to_not receive(:call) + + storage.deprecated_system = true + end + end + + context "when the system is set as deprecated" do + it "marks the system as deprecated" do + storage.deprecated_system = true + expect(storage.deprecated_system?).to eq(true) + end + + it "adds a deprecated system issue" do + expect(storage.issues).to be_empty + + storage.deprecated_system = true + + expect(storage.issues).to include( + an_object_having_attributes(description: /system devices have changed/) + ) + end + end + + context "when the system is set as not deprecated" do + it "marks the system as not deprecated" do + storage.deprecated_system = false + + expect(storage.deprecated_system?).to eq(false) + end + + it "does not add a deprecated system issue" do + storage.deprecated_system = false + + expect(storage.issues).to_not include( + an_object_having_attributes(description: /system devices have changed/) + ) + end + end + end + + describe "#probe" do before do + allow(Y2Storage::StorageManager).to receive(:instance).and_return(y2storage_manager) + allow(Agama::Storage::Proposal).to receive(:new).and_return(proposal) + allow(Agama::Storage::ISCSI::Manager).to receive(:new).and_return(iscsi) + + allow(y2storage_manager).to receive(:raw_probed).and_return(raw_devicegraph) + + allow(proposal).to receive(:issues).and_return(proposal_issues) + allow(proposal).to receive(:available_devices).and_return(devices) + allow(proposal).to receive(:settings).and_return(settings) + allow(proposal).to receive(:calculate) + allow(config).to receive(:pick_product) allow(iscsi).to receive(:activate) allow(y2storage_manager).to receive(:activate) @@ -85,6 +159,24 @@ allow(y2storage_manager).to receive(:probe) end + let(:raw_devicegraph) do + instance_double(Y2Storage::Devicegraph, probing_issues: probing_issues) + end + + let(:proposal) { Agama::Storage::Proposal.new(logger, config) } + + let(:iscsi) { Agama::Storage::ISCSI::Manager.new } + + let(:devices) { [disk1, disk2] } + let(:settings) { nil } + + let(:disk1) { instance_double(Y2Storage::Disk, name: "/dev/vda") } + let(:disk2) { instance_double(Y2Storage::Disk, name: "/dev/vdb") } + + let(:probing_issues) { [Y2Storage::Issue.new("probing issue")] } + + let(:proposal_issues) { [Agama::Issue.new("proposal issue")] } + it "probes the storage devices and calculates a proposal" do expect(config).to receive(:pick_product).with("ALP") expect(iscsi).to receive(:activate) @@ -101,7 +193,47 @@ storage.deprecated_system = true storage.probe - expect(storage.deprecated_system).to eq(false) + expect(storage.deprecated_system?).to eq(false) + end + + it "adds the probing issues" do + storage.probe + + expect(storage.issues).to include( + an_object_having_attributes(description: /probing issue/) + ) + end + + it "adds the proposal issues" do + storage.probe + + expect(storage.issues).to include( + an_object_having_attributes(description: /proposal issue/) + ) + end + + context "if there are available devices" do + let(:devices) { [disk1] } + + it "does not add an issue for available devices" do + storage.probe + + expect(storage.issues).to_not include( + an_object_having_attributes(description: /no suitable device/) + ) + end + end + + context "if there are not available devices" do + let(:devices) { [] } + + it "adds an issue for available devices" do + storage.probe + + expect(storage.issues).to include( + an_object_having_attributes(description: /no suitable device/) + ) + end end context "when there are settings from a previous proposal" do @@ -229,30 +361,7 @@ end end - describe "#validate" do - let(:errors) { [double("error 1")] } - let(:proposal) do - instance_double(Agama::Storage::Proposal, validate: errors) - end - - before do - allow(Agama::Storage::Proposal).to receive(:new).and_return(proposal) - end - - it "returns the proposal errors" do - expect(storage.validate).to eq(errors) - end - - context "if the system is deprecated" do - before do - storage.deprecated_system = true - end - - it "includes an error" do - error = storage.validate.find { |e| e.message.match?(/devices have changed/) } + include_examples "progress" - expect(error).to_not be_nil - end - end - end + include_examples "issues" end diff --git a/service/test/agama/storage/proposal_test.rb b/service/test/agama/storage/proposal_test.rb index 27740e83d0..709e9d90d1 100644 --- a/service/test/agama/storage/proposal_test.rb +++ b/service/test/agama/storage/proposal_test.rb @@ -53,6 +53,7 @@ let(:y2storage_proposal) do instance_double(Y2Storage::MinGuidedProposal, propose: true, failed?: failed) end + let(:failed) { false } describe "#calculate" do @@ -100,18 +101,16 @@ end it "runs all the callbacks" do - var1 = 5 - var2 = 5 - proposal.on_calculate do - var1 += 1 - end - proposal.on_calculate { var2 *= 2 } + callback1 = proc {} + callback2 = proc {} + + proposal.on_calculate(&callback1) + proposal.on_calculate(&callback2) + + expect(callback1).to receive(:call) + expect(callback2).to receive(:call) - expect(var1).to eq 5 - expect(var2).to eq 5 proposal.calculate - expect(var1).to eq 6 - expect(var2).to eq 10 end it "stores the given settings" do @@ -348,39 +347,22 @@ end end - describe "#validate" do - let(:sda) { instance_double(Y2Storage::Disk, name: "/dev/sda") } - let(:available_devices) { [sda] } - let(:candidate_devices) { ["/dev/sda"] } - + describe "#issues" do before do + allow(subject).to receive(:proposal).and_return(y2storage_proposal) allow(subject).to receive(:available_devices).and_return(available_devices) allow(subject).to receive(:candidate_devices).and_return(candidate_devices) - allow(subject).to receive(:proposal).and_return(y2storage_proposal) end - context "when the proposal was successful" do - let(:failed) { false } - - it "returns an empty list" do - expect(subject.validate).to eq([]) - end - end + let(:sda) { instance_double(Y2Storage::Disk, name: "/dev/sda") } + let(:available_devices) { [sda] } + let(:candidate_devices) { [] } context "when the proposal does not exist yet" do let(:y2storage_proposal) { nil } it "returns an empty list" do - expect(subject.validate).to eq([]) - end - end - - context "when there are not available storage devices" do - let(:available_devices) { [] } - - it "returns a list of errors including the expected error" do - error = subject.validate.find { |e| e.message.match?(/no suitable device/) } - expect(error).to_not be_nil + expect(subject.issues).to eq([]) end end @@ -388,17 +370,19 @@ let(:candidate_devices) { [] } it "returns a list of errors including the expected error" do - error = subject.validate.find { |e| e.message.match?(/No devices are selected/) } - expect(error).to_not be_nil + expect(subject.issues).to include( + an_object_having_attributes(description: /No devices are selected/) + ) end end - context "when any candidate device is missing" do + context "when some candidate device is missing" do let(:candidate_devices) { ["/dev/vda"] } it "returns a list of errors including the expected error" do - error = subject.validate.find { |e| e.message.match?(/not found in the system/) } - expect(error).to_not be_nil + expect(subject.issues).to include( + an_object_having_attributes(description: /devices are not found/) + ) end end @@ -406,8 +390,9 @@ let(:failed) { true } it "returns a list of errors including the expected error" do - error = subject.validate.find { |e| e.message.match?(/Cannot accommodate/) } - expect(error).to_not be_nil + expect(subject.issues).to include( + an_object_having_attributes(description: /Cannot accommodate/) + ) end end end diff --git a/service/test/agama/with_issues_examples.rb b/service/test/agama/with_issues_examples.rb new file mode 100644 index 0000000000..e3ce105822 --- /dev/null +++ b/service/test/agama/with_issues_examples.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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_relative "../test_helper" +require "agama/issue" + +shared_examples "issues" do + describe "#issues=" do + let(:issues) { [Agama::Issue.new("Issue 1"), Agama::Issue.new("Issue 2")] } + + it "sets the given list of issues" do + expect(subject.issues).to be_empty + + subject.issues = issues + + expect(subject.issues).to contain_exactly( + an_object_having_attributes(description: /Issue 1/), + an_object_having_attributes(description: /Issue 2/) + ) + end + + it "executes the on_issues_change callbacks" do + callback = proc {} + subject.on_issues_change(&callback) + + expect(callback).to receive(:call) + + subject.issues = issues + end + end +end From d832985dfe5cb271ee225549921c338fc57a51c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:45:57 +0100 Subject: [PATCH 5/7] [service] Adapt storage client --- service/lib/agama/dbus/clients/storage.rb | 4 +- service/lib/agama/dbus/clients/with_issues.rb | 54 +++++++++++++ service/lib/agama/manager.rb | 2 +- .../test/agama/dbus/clients/storage_test.rb | 4 +- .../dbus/clients/with_issues_examples.rb | 76 +++++++++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 service/lib/agama/dbus/clients/with_issues.rb create mode 100644 service/test/agama/dbus/clients/with_issues_examples.rb diff --git a/service/lib/agama/dbus/clients/storage.rb b/service/lib/agama/dbus/clients/storage.rb index 218f9eb34f..bff3080aab 100644 --- a/service/lib/agama/dbus/clients/storage.rb +++ b/service/lib/agama/dbus/clients/storage.rb @@ -22,7 +22,7 @@ require "agama/dbus/clients/base" require "agama/dbus/clients/with_service_status" require "agama/dbus/clients/with_progress" -require "agama/dbus/clients/with_validation" +require "agama/dbus/clients/with_issues" module Agama module DBus @@ -31,7 +31,7 @@ module Clients class Storage < Base include WithServiceStatus include WithProgress - include WithValidation + include WithIssues STORAGE_IFACE = "org.opensuse.Agama.Storage1" private_constant :STORAGE_IFACE diff --git a/service/lib/agama/dbus/clients/with_issues.rb b/service/lib/agama/dbus/clients/with_issues.rb new file mode 100644 index 0000000000..a4a1dc363e --- /dev/null +++ b/service/lib/agama/dbus/clients/with_issues.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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 "agama/issue" + +module Agama + module DBus + # Mixin to include in the clients of services that implement the Issues interface + module WithIssues + ISSUES_IFACE = "org.opensuse.Agama1.Issues" + private_constant :ISSUES_IFACE + + # Returns the issues + # + # @return [Array] + def issues + sources = [nil, Issue::Source::SYSTEM, Issue::Source::CONFIG] + severities = [Issue::Severity::WARN, Issue::Severity::ERROR] + + dbus_object[ISSUES_IFACE]["All"].map do |dbus_issue| + Issue.new(dbus_issue[0], + details: dbus_issue[1], + source: sources[dbus_issue[2]], + severity: severities[dbus_issue[3]]) + end + end + + # Determines whether there are errors + # + # @return [Boolean] + def errors? + issues.any?(&:error?) + end + end + end +end diff --git a/service/lib/agama/manager.rb b/service/lib/agama/manager.rb index e98fe2f0b3..8462ca00b9 100644 --- a/service/lib/agama/manager.rb +++ b/service/lib/agama/manager.rb @@ -178,7 +178,7 @@ def on_services_status_change(&block) # # @return [Boolean] def valid? - [storage, users, software].all?(&:valid?) + [users, software].all?(&:valid?) && !storage.errors? end # Collects the logs and stores them into an archive diff --git a/service/test/agama/dbus/clients/storage_test.rb b/service/test/agama/dbus/clients/storage_test.rb index c44240344f..c6c39069c0 100644 --- a/service/test/agama/dbus/clients/storage_test.rb +++ b/service/test/agama/dbus/clients/storage_test.rb @@ -20,7 +20,7 @@ # find current contact information at www.suse.com. require_relative "../../../test_helper" -require_relative "with_validation_examples" +require_relative "with_issues_examples" require "agama/dbus/clients/storage" require "dbus" @@ -194,5 +194,5 @@ end end - include_examples "validation" + include_examples "issues" end diff --git a/service/test/agama/dbus/clients/with_issues_examples.rb b/service/test/agama/dbus/clients/with_issues_examples.rb new file mode 100644 index 0000000000..e599db742f --- /dev/null +++ b/service/test/agama/dbus/clients/with_issues_examples.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] 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_relative "../../../test_helper" +require "agama/issue" + +shared_examples "issues" do + before do + allow(dbus_object).to receive(:path).and_return("/org/opensuse/Agama/Test") + allow(dbus_object).to receive(:[]).with("org.opensuse.Agama1.Issues") + .and_return(issues_properties) + end + + let(:issues_properties) { { "All" => issues } } + + let(:issues) { [issue1, issue2] } + let(:issue1) { ["Issue 1", "Details 1", 1, 0] } + let(:issue2) { ["Issue 2", "Details 2", 2, 1] } + + describe "#issues" do + it "returns the list of issues" do + expect(subject.issues).to all(be_a(Agama::Issue)) + + expect(subject.issues).to contain_exactly( + an_object_having_attributes( + description: "Issue 1", + details: "Details 1", + source: Agama::Issue::Source::SYSTEM, + severity: Agama::Issue::Severity::WARN + ), + an_object_having_attributes( + description: "Issue 2", + details: "Details 2", + source: Agama::Issue::Source::CONFIG, + severity: Agama::Issue::Severity::ERROR + ) + ) + end + end + + describe "#errors?" do + context "if there is any error" do + let(:issues) { [issue2] } + + it "returns true" do + expect(subject.errors?).to eq(true) + end + end + + context "if there is no error" do + let(:issues) { [issue1] } + + it "returns false" do + expect(subject.errors?).to eq(false) + end + end + end +end From 3a29e941c4422b3c94acc98ed14f6be3820f5541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 16:51:20 +0100 Subject: [PATCH 6/7] [service] Changelog --- service/package/rubygem-agama.changes | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/service/package/rubygem-agama.changes b/service/package/rubygem-agama.changes index 6f19e96f71..ea29bfb5f3 100644 --- a/service/package/rubygem-agama.changes +++ b/service/package/rubygem-agama.changes @@ -1,9 +1,16 @@ +------------------------------------------------------------------- +Wed Apr 26 15:48:41 UTC 2023 - José Iván López González + +- Add D-Bus API for managing issues. +- Replace validation interface by issues in the storage service. +- gh#openSUSE/agama#548 + ------------------------------------------------------------------- Fri Apr 14 13:13:56 UTC 2023 - José Iván López González - Modify default storage settings. - Fix issue with volume templates. -- gh#openSUSE/agama/521 +- gh#openSUSE/agama#521 ------------------------------------------------------------------- Mon Apr 10 10:14:39 UTC 2023 - Imobach Gonzalez Sosa From d86b948649db3d1708d9222f0843d3ccb075cec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Thu, 27 Apr 2023 09:08:05 +0100 Subject: [PATCH 7/7] [service] Improve doc --- service/lib/agama/dbus.rb | 2 +- service/lib/agama/dbus/interfaces/issues.rb | 2 +- service/lib/agama/issue.rb | 17 +++++++++++++++-- service/lib/agama/with_issues.rb | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/service/lib/agama/dbus.rb b/service/lib/agama/dbus.rb index 45354f6c27..f4870b8eac 100644 --- a/service/lib/agama/dbus.rb +++ b/service/lib/agama/dbus.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2022-2023] SUSE LLC # # All Rights Reserved. # diff --git a/service/lib/agama/dbus/interfaces/issues.rb b/service/lib/agama/dbus/interfaces/issues.rb index 758edba54f..09ccc17d9a 100644 --- a/service/lib/agama/dbus/interfaces/issues.rb +++ b/service/lib/agama/dbus/interfaces/issues.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022] SUSE LLC +# Copyright (c) [2023] SUSE LLC # # All Rights Reserved. # diff --git a/service/lib/agama/issue.rb b/service/lib/agama/issue.rb index 067cce1a7c..18630a4d29 100644 --- a/service/lib/agama/issue.rb +++ b/service/lib/agama/issue.rb @@ -20,7 +20,20 @@ # find current contact information at www.suse.com. module Agama - # Represents an issue + # This class represents an issue in Agama + # + # An issue is used for notifying about some unexpected or problematic situation, for example, to + # indicate that there is no device for installing the system. + # + # Issues have a description, details, source and severity: + # * Description: describes the issue, typically with a single sentence. + # * Details: provides more details about the problem. It is useful to report the output of a + # command failure. + # * Source: indicates the source of the problem. In Agama, the issues usually comes from some + # unexpected situation in the system (e.g., missing device, etc) or from a wrong config (e.g., + # missing user, etc). + # * Severity: sets the severity of the issue. For now, issues could have warn or error severity. + # Error severity indicates that the installation cannot start. class Issue # Description of the issue # @@ -48,7 +61,7 @@ module Source CONFIG = :config end - # Defines different severities + # Defines different severity levels module Severity WARN = :warn ERROR = :error diff --git a/service/lib/agama/with_issues.rb b/service/lib/agama/with_issues.rb index cdec10aa22..950ddce6bb 100644 --- a/service/lib/agama/with_issues.rb +++ b/service/lib/agama/with_issues.rb @@ -20,7 +20,7 @@ # find current contact information at www.suse.com. module Agama - # Mixin for managing issues + # Mixin for managing issues, see {Issue} module WithIssues # The list of current issues #