From 8910153a1b0f81b1d2dad59ff51ae9d7e0aa7279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Mon, 2 Sep 2019 21:33:18 +0200 Subject: [PATCH] Added Y2Packager::MediumType class --- src/lib/y2packager/medium_type.rb | 102 +++++++++++++++++++++++++ src/lib/y2packager/product_location.rb | 8 +- test/medium_type_test.rb | 80 +++++++++++++++++++ 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/lib/y2packager/medium_type.rb create mode 100644 test/medium_type_test.rb diff --git a/src/lib/y2packager/medium_type.rb b/src/lib/y2packager/medium_type.rb new file mode 100644 index 000000000..ebeca8ae1 --- /dev/null +++ b/src/lib/y2packager/medium_type.rb @@ -0,0 +1,102 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) 2019 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. +# ------------------------------------------------------------------------------ + +require "yast" + +require "y2packager/product_location" +require "y2packager/repomd_downloader" + +Yast.import "InstURL" + +module Y2Packager + # This class scans the installation medium type. + class MediumType + extend Yast::Logger + + # Type of the installation medium + # + # @return [Symbol] Symbol describing the medium, one of `:offline`, + # `:online` or `:standard` + def self.type + @type ||= detect_medium_type + end + + # Is the medium an online installation medium? (SLE Online) + # The online installation medium contains no repository + # or a repository without any base product. + def self.online? + type == :online + end + + # Is the medium an offline installation medium? (SLE Offline) + # The offline installation medium contains several installation repositories. + # (At least one base product and one module/extension, usually there are + # several base products and many modules/extensions.) + def self.offline? + type == :offline + end + + # Is the medium an standard installation medium? (openSUSE Leap) + # The standard installation medium contains a single repository + # with at least one base product. (Usually there is only one base product.) + def self.standard? + type == :standard + end + + ############################################################################################## + + # + # Detect the medium type. + # + # @return [Symbol] Symbol describing the medium, one of `:offline`, + # `:online` or `:standard` + def self.detect_medium_type + url = Yast::InstURL.installInf2Url("") + + raise "The installation URL is not set" if url.nil? || url.empty? + + # scan the number of the products in the media.1/products file + downloader = Y2Packager::RepomdDownloader.new(url) + product_repos = downloader.product_repos + + # the online medium should not contain any repository + # TODO: how to detect an invalid installation URL or a broken medium?? + if product_repos.empty? + log.info("Detected medium type: online (no repository on the medium)") + return :online + end + + # the offline medium contains several modules and extensions + if product_repos.size > 1 + log.info("Detected medium type: offline (found #{product_repos.size} product repositories)") + return :offline + end + + # preferred base product for scanning the products on the medium + base_product = nil + # run the scan even when there is only one repository on the medium + force_scan = true + base_products = Y2Packager::ProductLocation.scan(url, base_product, force_scan) + .select { |p| p.details&.base } + + if base_products.empty? + log.info("Detected medium type: online (no base product found on the medium)") + :online + else + log.info("Detected medium type: standard (found #{base_products.size} base products)") + :standard + end + end + + private_class_method :detect_medium_type + end +end diff --git a/src/lib/y2packager/product_location.rb b/src/lib/y2packager/product_location.rb index 567e1068a..df1d5ce6b 100644 --- a/src/lib/y2packager/product_location.rb +++ b/src/lib/y2packager/product_location.rb @@ -42,16 +42,20 @@ class ProductLocation # @param base_product [String,nil] The base product used for evaluating the # product dependencies, if nil the solver can select any product to satisfy # the dependencies. + # @param force_scan [Boolean] force evaluating the products (and their + # dependencies) even when there is only one repository on the medium. + # For the performance reasons the default is `false`, set `true` for + # special cases. # # @return [Array] The found products # - def self.scan(url, base_product = nil) + def self.scan(url, base_product = nil, force_scan = false) log.info "Scanning #{Yast::URL.HidePassword(url)} for products..." downloader = Y2Packager::RepomdDownloader.new(url) # Skip the scan if there is none or just one repository, the repository selection # is displayed only when there are at least 2 repositories. - return [] if downloader.product_repos.size < 2 + return [] if downloader.product_repos.size < 2 && !force_scan pool = Y2Packager::SolvablePool.new diff --git a/test/medium_type_test.rb b/test/medium_type_test.rb new file mode 100644 index 000000000..bf4da1360 --- /dev/null +++ b/test/medium_type_test.rb @@ -0,0 +1,80 @@ +require_relative "test_helper" +require "y2packager/medium_type" + +describe Y2Packager::MediumType do + Yast.import "Pkg" + + let(:repo_url) { "http://example.com/repo" } + + before do + allow(Yast::InstURL).to receive(:installInf2Url).and_return(repo_url) + end + + after do + # the computed value is cached, we need to reset it manually for the next test + described_class.instance_variable_set(:@type, nil) + end + + describe "#type" do + it "raises an exception when the installation URL is nil" do + expect(Yast::InstURL).to receive(:installInf2Url).and_return(nil) + expect { described_class.type }.to raise_exception(/The installation URL is not set/) + end + + it "raises an exception when the installation URL is empty" do + expect(Yast::InstURL).to receive(:installInf2Url).and_return("") + expect { described_class.type }.to raise_exception(/The installation URL is not set/) + end + + it "returns :online if no repository is found on the medium" do + expect_any_instance_of(Y2Packager::RepomdDownloader) + .to receive(:product_repos).and_return([]) + + expect(described_class.type).to eq(:online) + end + + it "returns :offline if at least two repositories are found on the medium" do + expect_any_instance_of(Y2Packager::RepomdDownloader) + .to receive(:product_repos).and_return( + [ + ["Basesystem-Module 15.1-0", "/Module-Basesystem"], + ["SLES15-SP1 15.1-0", "/Product-SLES"] + ] + ) + + expect(described_class.type).to eq(:offline) + end + + context "only one repository on the installation medium" do + before do + expect_any_instance_of(Y2Packager::RepomdDownloader) + .to receive(:product_repos).and_return( + [ + ["SLES15-SP1 15.1-0", "/"] + ] + ) + end + + it "returns :online if the repository does not contain any base product" do + expect(Y2Packager::ProductLocation).to receive(:scan).and_return([]) + expect(described_class.type).to eq(:online) + end + + it "returns :standard if the repository contains any base product" do + details = Y2Packager::ProductLocationDetails.new( + product: "SLES", + summary: "SUSE Linux Enterprise Server 15 SP1", + description: "SUSE Linux Enterprise offers a comprehensive...", + order: 200, + base: true, + depends_on: [], + product_package: "sles-release" + ) + prod = Y2Packager::ProductLocation.new("/", "/", product: details) + + expect(Y2Packager::ProductLocation).to receive(:scan).and_return([prod]) + expect(described_class.type).to eq(:standard) + end + end + end +end