From 8dd8f72b12540ecdab1269b743620c38d098539a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 25 Aug 2015 15:56:44 +0200 Subject: [PATCH] Handle not available products (bsc#942892) ...when using a SMT server for running online migration --- package/yast2-registration.changes | 6 + .../ui/migration_selection_dialog.rb | 107 +++++++++++++++--- test/migration_selection_dialog_test.rb | 34 +++++- 3 files changed, 129 insertions(+), 18 deletions(-) diff --git a/package/yast2-registration.changes b/package/yast2-registration.changes index 5ba595bb4..0acf7f17a 100644 --- a/package/yast2-registration.changes +++ b/package/yast2-registration.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Tue Aug 25 13:54:46 UTC 2015 - lslezak@suse.cz + +- Handle not available products when using a SMT server for running + online migration (bsc#942892) + ------------------------------------------------------------------- Fri Aug 21 11:38:22 UTC 2015 - lslezak@suse.cz diff --git a/src/lib/registration/ui/migration_selection_dialog.rb b/src/lib/registration/ui/migration_selection_dialog.rb index 789ed49d7..16b68b874 100644 --- a/src/lib/registration/ui/migration_selection_dialog.rb +++ b/src/lib/registration/ui/migration_selection_dialog.rb @@ -17,6 +17,7 @@ require "yast" require "registration/migration_sorter" require "registration/sw_mgmt" +require "registration/url_helpers" module Registration module UI @@ -28,6 +29,8 @@ class MigrationSelectionDialog Yast.import "UI" Yast.import "Wizard" + Yast.import "Report" + Yast.import "HTML" attr_accessor :selected_migration, :manual_repo_selection, :installed_products @@ -71,18 +74,43 @@ def run update_details + handle_user_input + end + + private + + attr_accessor :migrations + + # the main loop for handling the user inut + # @return [Symbol] the UI symbol + def handle_user_input loop do ret = Yast::UI.UserInput - update_details if ret == :migration_targets - store_values if ret == :next + + case ret + when :migration_targets + update_details + when :next + if valid_migration? + store_values + else + report_unavailable_migration + next + end + end return ret if [:next, :back, :cancel, :abort].include?(ret) end end - private - - attr_accessor :migrations + # is the current selected migration valid? (a migration is selected and + # all products are available) + # @return [Boolean] true if the migration can be used + def valid_migration? + current = current_migration + # available is nil (not set) or true + current && current.all? { |p| p.available.nil? || p.available } + end def add_registered_addons extra = Addon.registered_not_installed.map { |addon| SwMgmt.remote_product(addon) } @@ -156,8 +184,31 @@ def migration_details(idx) "

" + _("Migration Summary") + "

" end + # create a product summary for the details widget + # @return [String] product summary def product_summary(product, installed_product) product_name = CGI.escapeHTML(product.friendly_name) + log.info "creating summary for #{product} and #{installed_product}" + + # explicitly check for false, the flag is not returned by SCC, this is + # a SMT specific check (in SCC all products are implicitly available) + if product.available == false + # a product can be unavailable only when using SMT, the default + # SCC URL should be never used + url = UrlHelpers.registration_url || SUSE::Connect::YaST::DEFAULT_URL + + # TRANSLATORS: An error message displayed in the migration details. + # The product has not been mirrored to the SMT server and cannot be used + # for migration. The SMT admin has to mirror the product to allow + # using the selected migration. + # %{url} is the URL of the registration server (SMT) + # %{product} is a full product name, e.g. "SUSE Linux Enterprise Server 12" + return Yast::HTML.Colorize( + _("ERROR: Product %{product} is not available at the " \ + "registration server (%{url}). Make the product available " \ + "to allow using this migration.") % { product: product_name, + url: url }, "red") + end if !installed_product # this is rather a theoretical case, but anyway.... @@ -166,46 +217,70 @@ def product_summary(product, installed_product) return _("%s will be installed.") % product_name end - installed_version = installed_product["version_version"] + product_change_summary(installed_product, product) + end - if installed_version == product.version + # create a summary for changed product + # @param [Hash] old_product the old installed libzypp product + # @param [OpenStruct] the new target product + # @return [String] RichText summary + def product_change_summary(old_product, new_product) + new_product_name = CGI.escapeHTML(new_product.friendly_name) + installed_version = old_product["version_version"] + + if installed_version == new_product.version # TRANSLATORS: Summary message, rich text format # %s is a product name, e.g. "SUSE Linux Enterprise Server 12" - return _("%s stays unchanged.") % product_name + return _("%s stays unchanged.") % new_product_name end - old_product_name = SwMgmt.product_label(installed_product) + old_product_name = SwMgmt.product_label(old_product) # use Gem::Version for version compare - if Gem::Version.new(installed_version) < Gem::Version.new(product.version) + if Gem::Version.new(installed_version) < Gem::Version.new(new_product.version) # TRANSLATORS: Summary message, rich text format # %{old_product} is a product name, e.g. "SUSE Linux Enterprise Server 12" # %{new_product} is a product name, e.g. "SUSE Linux Enterprise Server 12 SP1 x86_64" return _("%{old_product} will be upgraded to %{new_product}.") \ - % { old_product: old_product_name, new_product: product_name } + % { old_product: old_product_name, new_product: new_product_name } else # TRANSLATORS: Summary message, rich text format # %{old_product} and %{new_product} are product names return _("%{old_product} will be downgraded to %{new_product}.") \ - % { old_product: old_product_name, new_product: product_name } + % { old_product: old_product_name, new_product: new_product_name } end end # store the current UI values def store_values - selected = Yast::UI.QueryWidget(:migration_targets, :CurrentItem) - self.selected_migration = migrations[selected] - log.info "Selected migration: #{selected_migration}" - + self.selected_migration = current_migration self.manual_repo_selection = Yast::UI.QueryWidget(:manual_repos, :Value) end + # return the currently selected migration + # @return [Array] the selected migration target + def current_migration + current_item = Yast::UI.QueryWidget(:migration_targets, :CurrentItem) + migration = migrations[current_item] + log.info "Selected migration: #{migration}" + migration + end + def sorted_migrations # sort the products in each migration migrations.map do |migration| migration.sort(&::Registration::MIGRATION_SORTER) end end + + # display an error popup + def report_unavailable_migration + # TRANSLATORS: an error popup message + Yast::Report.Error(_("The selected migration contains a product\n" \ + "which is not available at the registration server.\n\n" \ + "Select a different migration target or make the missing products\n" \ + "available at the registration server.")) + end end end end diff --git a/test/migration_selection_dialog_test.rb b/test/migration_selection_dialog_test.rb index f717cecbf..6a49ddb0a 100755 --- a/test/migration_selection_dialog_test.rb +++ b/test/migration_selection_dialog_test.rb @@ -29,10 +29,9 @@ end it "saves the entered values when clicking Next" do - # user pressed the "Abort" button expect(Yast::UI).to receive(:UserInput).and_return(:next) expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem) - .and_return(0).twice + .and_return(0).at_least(1) expect(Yast::UI).to receive(:QueryWidget).with(:manual_repos, :Value).and_return(true) dialog = subject.new(migration_products, []) @@ -42,5 +41,36 @@ expect(dialog.selected_migration).to eq(migration_products.first) expect(dialog.manual_repo_selection).to eq(true) end + + it "displays an error when the selected migration contains unavailable product" do + # user pressed the "Abort" button after displaying the error message + expect(Yast::UI).to receive(:UserInput).and_return(:next, :abort) + expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem) + .and_return(0).at_least(1) + expect(Yast::Report).to receive(:Error) + .with(/is not available at the registration server/) + allow(Registration::UrlHelpers).to receive(:registration_url) + .and_return("http://example.com") + + migrations = migration_products + # make one product not available + migrations.first.first.available = false + + dialog = subject.new(migrations, []) + expect(dialog.run).to eq(:abort) + end + + it "displays a product summary" do + expect(Yast::UI).to receive(:UserInput).and_return(:next) + expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem) + .and_return(0).at_least(1) + expect(Yast::UI).to receive(:QueryWidget).with(:manual_repos, :Value).and_return(true) + + # load just the SLES12 product from that file + installed = load_yaml_fixture("products_legacy_installation.yml")[1] + + dialog = subject.new(migration_products, [installed]) + expect(dialog.run).to eq(:next) + end end end