diff --git a/app/controllers/katello/api/v2/repositories_controller.rb b/app/controllers/katello/api/v2/repositories_controller.rb index 89aa5afde6c..de1fae42e92 100644 --- a/app/controllers/katello/api/v2/repositories_controller.rb +++ b/app/controllers/katello/api/v2/repositories_controller.rb @@ -62,6 +62,7 @@ def custom_index_relation(collection) param :deb_releases, String, :desc => N_("whitespace-separated list of releases to be synced from deb-archive") param :deb_components, String, :desc => N_("whitespace-separated list of repo components to be synced from deb-archive") param :deb_architectures, String, :desc => N_("whitespace-separated list of architectures to be synced from deb-archive") + param :deb_errata_url, String, :desc => N_("URL to a deb errata service (use only on the security repositories)") param :ignorable_content, Array, :desc => N_("List of content units to ignore while syncing a yum repository. Must be subset of %s") % RootRepository::IGNORABLE_CONTENT_UNIT_TYPES.join(",") param :ansible_collection_requirements, String, :desc => N_("Contents of requirement yaml file to sync from URL") param :ansible_collection_auth_url, String, :desc => N_("The URL to receive a session token from, e.g. used with Automation Hub.") @@ -580,7 +581,7 @@ def repository_params keys = [:download_policy, :mirroring_policy, :sync_policy, :arch, :verify_ssl_on_sync, :upstream_password, :upstream_username, :download_concurrency, :upstream_authentication_token, :metadata_expire, {:os_versions => []}, :deb_releases, :deb_components, :deb_architectures, :description, - :http_proxy_policy, :http_proxy_id, :retain_package_versions_count, {:ignorable_content => []} + :http_proxy_policy, :http_proxy_id, :retain_package_versions_count, {:ignorable_content => []}, :deb_errata_url ] keys += [{:include_tags => []}, {:exclude_tags => []}, :docker_upstream_name] if params[:action] == 'create' || @repository&.docker? keys += [:ansible_collection_requirements, :ansible_collection_auth_url, :ansible_collection_auth_token] if params[:action] == 'create' || @repository&.ansible_collection? @@ -645,6 +646,7 @@ def construct_repo_from_params(repo_params) # rubocop:disable Metrics/AbcSize root.deb_releases = repo_params[:deb_releases] if repo_params[:deb_releases] root.deb_components = repo_params[:deb_components] if repo_params[:deb_components] root.deb_architectures = repo_params[:deb_architectures] if repo_params[:deb_architectures] + root.deb_errata_url = repo_params[:deb_errata_url] if repo_params[:deb_errata_url] end if root.ansible_collection? diff --git a/app/lib/actions/katello/content_view/incremental_updates.rb b/app/lib/actions/katello/content_view/incremental_updates.rb index 50400b5027e..12af3d91649 100644 --- a/app/lib/actions/katello/content_view/incremental_updates.rb +++ b/app/lib/actions/katello/content_view/incremental_updates.rb @@ -91,6 +91,7 @@ def total_counts(input) total_count[:errata_count] = added_units[:erratum].try(:count) total_count[:modulemd_count] = added_units[:modulemd].try(:count) total_count[:rpm_count] = added_units[:rpm].try(:count) + total_count[:deb_count] = added_units[:deb].try(:count) end end end @@ -134,6 +135,10 @@ def content_output_collection(total_count) rpm = _(" %{package_count} Package(s)" % {:package_count => total_count[:rpm_count]}) content << rpm end + if total_count[:deb_count] && total_count[:deb_count] > 0 + deb = _(" %{deb_package_count} Package(s)" % {:deb_package_count => total_count[:deb_count]}) + content << deb + end content end diff --git a/app/lib/actions/katello/content_view/presenters/incremental_updates_presenter.rb b/app/lib/actions/katello/content_view/presenters/incremental_updates_presenter.rb index 2176a581ce2..26702ee49a4 100644 --- a/app/lib/actions/katello/content_view/presenters/incremental_updates_presenter.rb +++ b/app/lib/actions/katello/content_view/presenters/incremental_updates_presenter.rb @@ -6,7 +6,8 @@ class IncrementalUpdatesPresenter < Helpers::Presenter::Base HUMANIZED_TYPES = { ::Katello::Erratum::CONTENT_TYPE => "Errata", ::Katello::ModuleStream::CONTENT_TYPE => "Module Streams", - ::Katello::Rpm::CONTENT_TYPE => "Packages" + ::Katello::Rpm::CONTENT_TYPE => "RPM Packages", + ::Katello::Deb::CONTENT_TYPE => "Deb Packages" }.freeze def humanized_output @@ -25,7 +26,7 @@ def humanized_content if cvv humanized_lines << "Content View: #{cvv.content_view.name} version #{cvv.version}" humanized_lines << _("Added Content:") - [::Katello::Erratum, ::Katello::ModuleStream, ::Katello::Rpm].each do |content_type| + [::Katello::Erratum, ::Katello::ModuleStream, ::Katello::Rpm, ::Katello::Deb].each do |content_type| unless output[:added_units][content_type::CONTENT_TYPE].blank? humanized_lines << " #{HUMANIZED_TYPES[content_type::CONTENT_TYPE]}:" humanized_lines += output[:added_units][content_type::CONTENT_TYPE].sort.map { |unit| " #{unit}" } diff --git a/app/lib/actions/katello/content_view_version/incremental_update.rb b/app/lib/actions/katello/content_view_version/incremental_update.rb index 77aae3af4d9..0f44d048022 100644 --- a/app/lib/actions/katello/content_view_version/incremental_update.rb +++ b/app/lib/actions/katello/content_view_version/incremental_update.rb @@ -1,7 +1,7 @@ module Actions module Katello module ContentViewVersion - class IncrementalUpdate < Actions::EntryAction + class IncrementalUpdate < Actions::EntryAction # rubocop:disable Metrics/ClassLength include ::Katello::ContentViewHelper attr_accessor :new_content_view_version, :new_content_view_version_id @@ -74,21 +74,34 @@ def plan(old_version, environments, options = {}) end concurrence do - if separated_repo_map[:pulp3_yum_multicopy].keys.flatten.present? - extended_repo_mapping = pulp3_repo_mapping(separated_repo_map[:pulp3_yum_multicopy], old_version) - unit_map = pulp3_content_mapping(content) + [:pulp3_deb_multicopy, :pulp3_yum_multicopy].each do |mapping| + if separated_repo_map[mapping].keys.flatten.present? + extended_repo_mapping = pulp3_repo_mapping(separated_repo_map[mapping], old_version) + local_content = {} + if mapping == :pulp3_deb_multicopy + local_content[:errata_ids], local_content[:deb_ids] = resolve_deb_errata(content, extended_repo_mapping) + end + + # makes sure that all keys in local_content are symbols! + # content is of type ActionController::Parameters and uses string and symbol keys synonymically. + # Hash (local_content) does not and we only access with symbol-keys, so make sure we only use symbols. + local_content.keys.union(content.keys.map(&:to_sym)).each do |content_type| + local_content[content_type] = local_content.fetch(content_type, []) + content.fetch(content_type, []) + end + unit_map = pulp3_content_mapping(local_content) - unless extended_repo_mapping.empty? || unit_map.values.flatten.empty? sequence do - # Pre-copy content if dest_repo is a soft copy of its library instance. - # Don't use extended_repo_mapping because the source repositories are library instances. - # We want the old CV snapshot repositories here so as to not pull in excess new content. - separated_repo_map[:pulp3_yum_multicopy].each do |source_repos, dest_repo| - if dest_repo.soft_copy_of_library? - source_repos.each do |source_repo| - # remove_all flag is set to cover the case of incrementally updating more than once with different content. - # Without it, content from the previous incremental update will be copied as well due to how Pulp repo versions work. - plan_action(Pulp3::Repository::CopyContent, source_repo, SmartProxy.pulp_primary, dest_repo, copy_all: true, remove_all: true) + unless extended_repo_mapping.empty? || unit_map.values.flatten.empty? + # Pre-copy content if dest_repo is a soft copy of its library instance. + # Don't use extended_repo_mapping because the source repositories are library instances. + # We want the old CV snapshot repositories here so as to not pull in excess new content. + separated_repo_map[mapping].each do |source_repos, dest_repo| + if dest_repo.soft_copy_of_library? + source_repos.each do |source_repo| + # remove_all flag is set to cover the case of incrementally updating more than once with different content. + # Without it, content from the previous incremental update will be copied as well due to how Pulp repo versions work. + plan_action(Pulp3::Repository::CopyContent, source_repo, SmartProxy.pulp_primary, dest_repo, copy_all: true, remove_all: true) + end end end end @@ -99,6 +112,19 @@ def plan(old_version, environments, options = {}) copy_repos(repository_mapping[source_repos]) end end + + separated_repo_map[mapping].each do |_source_repos, dest_repo| + next unless dest_repo.deb? + + # find errata belonging to this repo + errata = ::Katello::Erratum.with_identifiers(local_content[:errata_ids]).joins(:root_repositories).where(::Katello::RootRepository.table_name => {id: dest_repo.root}).distinct + if errata.present? + # attach deb errata to the dest_repo + copy_action_outputs << plan_action(Actions::Katello::Repository::CopyDebErratum, + target_repo_id: dest_repo.id, + erratum_ids: errata.pluck(:errata_id)).output + end + end end end end @@ -212,6 +238,36 @@ def components_repo_instances(old_version_repo, new_component_versions) end end + def resolve_deb_errata(content, extended_repo_mapping) + needed_errata = [] + needed_debs = [] + content[:errata_ids].each do |erratum_id| + extended_repo_mapping.each do |source_repos, _dest_repo| + source_repos.each do |source_repo| + re = ::Katello::RepositoryErratum.joins(:erratum).find_by(::Katello::Erratum.table_name => { errata_id: erratum_id }, repository_id: source_repo) + + next if re.nil? # Erratum not in Repository + + # find packages + # FIXME: if multiple packages with the same name exist, we have to make sure we install the newest version + # but (for now) at least a version bigger or equal the one from the erratum! + pkgs = ::Katello::Deb.joins(repositories: { repository_errata: { erratum: :deb_packages }}) + .where("#{::Katello::Deb.table_name}.name = #{::Katello::ErratumDebPackage.table_name}.name AND deb_version_cmp(#{::Katello::Deb.table_name}.version,#{::Katello::ErratumDebPackage.table_name}.version) >=0") + .where(::Katello::RepositoryErratum.table_name => { id: re }) + .distinct.pluck(:id) + errata = ::Katello::Erratum.joins({ repositories: :debs }, :deb_packages) + .where(::Katello::RepositoryErratum.table_name => { repository_id: source_repo }) + .where(::Katello::Deb.table_name => { id: pkgs }) + .where("#{::Katello::Deb.table_name}.name = #{::Katello::ErratumDebPackage.table_name}.name AND deb_version_cmp(#{::Katello::Deb.table_name}.version,#{::Katello::ErratumDebPackage.table_name}.version) >=0") + .distinct.pluck(:errata_id) + needed_errata.concat errata + needed_debs.concat pkgs + end + end + end + return needed_errata, needed_debs + end + def run content = { ::Katello::Erratum::CONTENT_TYPE => [], ::Katello::Rpm::CONTENT_TYPE => [], @@ -229,6 +285,7 @@ def run new_errata = new_repo.errata - (matched_old_repo&.errata || []) new_module_streams = new_repo.module_streams - (matched_old_repo&.module_streams || []) new_rpms = new_repo.rpms - (matched_old_repo&.rpms || []) + new_debs = new_repo.debs - (matched_old_repo&.debs || []) new_errata.each do |erratum| content[::Katello::Erratum::CONTENT_TYPE] << erratum.errata_id @@ -240,6 +297,9 @@ def run new_rpms.each do |rpm| content[::Katello::Rpm::CONTENT_TYPE] << rpm.nvra end + new_debs.each do |deb| + content[::Katello::Deb::CONTENT_TYPE] << deb.nva + end end end output[:added_units] = content @@ -281,10 +341,11 @@ def calculate_components(old_version, new_components) def generate_description(version, content) humanized_lines = [] - [::Katello::Erratum, ::Katello::Rpm].each do |content_type| + [::Katello::Erratum, ::Katello::Rpm, ::Katello::Deb].each do |content_type| unless content[content_type::CONTENT_TYPE].blank? humanized_lines << "#{HUMANIZED_TYPES[content_type::CONTENT_TYPE]}:" - humanized_lines += content[content_type::CONTENT_TYPE].sort.map { |unit| " #{unit}" } + #FIXME: solves duplicate Deb-Errata displayed, here (might need deeper inspection) + humanized_lines += content[content_type::CONTENT_TYPE].uniq.sort.map { |unit| " #{unit}" } end humanized_lines << '' end diff --git a/app/lib/actions/katello/repository/clone_contents.rb b/app/lib/actions/katello/repository/clone_contents.rb index 99eece0a757..814f919c820 100644 --- a/app/lib/actions/katello/repository/clone_contents.rb +++ b/app/lib/actions/katello/repository/clone_contents.rb @@ -18,6 +18,13 @@ def plan(source_repositories, new_repository, options) SmartProxy.pulp_primary, source_repositories, filters: filters, rpm_filenames: rpm_filenames, solve_dependencies: solve_dependencies) + + source_repositories.select(&:deb?).each do |repository| + plan_action(Actions::Katello::Repository::CopyDebErratum, + source_repo_id: repository.id, + target_repo_id: new_repository.id, + clean_target_errata: true) + end end matching_content = check_matching_content(new_repository, source_repositories) diff --git a/app/lib/actions/katello/repository/copy_deb_erratum.rb b/app/lib/actions/katello/repository/copy_deb_erratum.rb new file mode 100644 index 00000000000..5f731da4c80 --- /dev/null +++ b/app/lib/actions/katello/repository/copy_deb_erratum.rb @@ -0,0 +1,43 @@ +module Actions + module Katello + module Repository + class CopyDebErratum < Actions::Base + input_format do + param :source_repo_id + param :target_repo_id + param :erratum_ids + param :clean_target_errata + end + + def run + target_repo = ::Katello::Repository.find(input[:target_repo_id]) + + # drop all existing errata from target_repo (e.g. promoting LCENV back to an earlier version) + target_repo.repository_errata.destroy_all if input[:clean_target_errata] == true + + erratum_ids_to_copy = [] + if input[:source_repo_id].present? + erratum_ids_to_copy = ::Katello::Repository.find(input[:source_repo_id])&.erratum_ids + elsif input[:erratum_ids].present? + erratum_ids_to_copy = ::Katello::Erratum.where(errata_id: input[:erratum_ids]).pluck(:id) + end + erratum_ids_to_copy -= target_repo.erratum_ids + target_repo.erratum_ids |= erratum_ids_to_copy + target_repo.save + + # fake output to make foreman task presenter happy + if input[:erratum_ids].present? + units = [] + ::Katello::Erratum.find(erratum_ids_to_copy).each do |erratum| + units << { 'type_id' => 'erratum', 'unit_key' => { 'id' => erratum.pulp_id } } + erratum.deb_packages.map do |pkg| + units << { 'type_id' => 'deb', 'unit_key' => { 'name' => pkg.name, 'version' => pkg.version } } + end + end + output[:pulp_tasks] = [{ :result => { :units_successful => units } }] + end + end + end + end + end +end diff --git a/app/lib/actions/katello/repository/multi_clone_contents.rb b/app/lib/actions/katello/repository/multi_clone_contents.rb index 94eb294087a..ee69bfe4970 100644 --- a/app/lib/actions/katello/repository/multi_clone_contents.rb +++ b/app/lib/actions/katello/repository/multi_clone_contents.rb @@ -21,6 +21,12 @@ def plan(extended_repo_mapping, options) extended_repo_mapping.each do |source_repos, dest_repo_map| dest_repo_map[:matching_content] = check_matching_content(dest_repo_map[:dest_repo], source_repos) + if source_repos.first.deb? + plan_action(Actions::Katello::Repository::CopyDebErratum, + source_repo_id: source_repos.first.id, + target_repo_id: dest_repo_map[:dest_repo].id) + end + if generate_metadata metadata_generate(source_repos, dest_repo_map[:dest_repo], dest_repo_map[:filters], dest_repo_map[:matching_content]) end diff --git a/app/lib/actions/katello/repository/sync.rb b/app/lib/actions/katello/repository/sync.rb index c5686b2af82..6ae2af5d6eb 100644 --- a/app/lib/actions/katello/repository/sync.rb +++ b/app/lib/actions/katello/repository/sync.rb @@ -49,6 +49,7 @@ def plan(repo, options = {}) plan_action(Katello::Foreman::ContentUpdate, repo.environment, repo.content_view, repo) plan_action(Katello::Repository::FetchPxeFiles, :id => repo.id) concurrence do + plan_action(Katello::Repository::SyncDebErrata, repo, skip_metadata_check) if repo.deb? && repo.root.deb_errata_url.present? plan_action(Katello::Repository::ErrataMail, repo) plan_action(Actions::Katello::Applicability::Repository::Regenerate, :repo_ids => [repo.id]) if generate_applicability end diff --git a/app/lib/actions/katello/repository/sync_deb_errata.rb b/app/lib/actions/katello/repository/sync_deb_errata.rb new file mode 100644 index 00000000000..6ea5600d2b4 --- /dev/null +++ b/app/lib/actions/katello/repository/sync_deb_errata.rb @@ -0,0 +1,108 @@ +module Actions + module Katello + module Repository + class SyncDebErrata < Actions::EntryAction + def plan(repo, force = false) + plan_self(repo_id: repo.id, force_download: force) + end + + def run + repo = ::Katello::Repository.find(input[:repo_id]).root + proxy = repo.http_proxy + params = {} + params['releases'] = repo.deb_releases.split(' ').map { |comp| comp.split('/')[0] }.join(',') if repo.deb_releases + params['components'] = repo.deb_components.split(' ').join(',') if repo.deb_components + params['architectures'] = repo.deb_architectures.split(' ').join(',') if repo.deb_architectures + RestClient::Request.execute( + method: :get, + url: repo.deb_errata_url, + proxy: proxy&.full_url, + headers: { + params: params, + 'If-None-Match' => input[:force_download] ? nil : repo.deb_errata_url_etag + } + ) do |response, _request, _result, &block| + case response.code + when 200 + output[:etag] = response.headers[:etag] || '' + output[:modified] = true + output[:data] = response.body + when 304 # not modified + output[:modified] = false + else + response.return!(&block) + end + end + rescue => e + raise "Error while fetching errata information (#{e.to_s})" + end + + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def finalize + if output[:modified] + repo = ::Katello::Repository.find(input[:repo_id]) + erratum_list = JSON.parse(output[:data]) + # force re-attaching all errata if mirroring + if repo.root.mirroring_policy == ::Katello::RootRepository::MIRRORING_POLICY_CONTENT + ::Katello::RepositoryErratum.where(repository: repo).destroy_all + end + erratum_list.each do |data| + erratum = ::Katello::Erratum.find_or_create_by(errata_id: data['name'], pulp_id: data['name']) + erratum.with_lock do + erratum.title = data['title'] + erratum.summary = data['summary'] || '' + erratum.description = data['description'] + erratum.issued = data['issued'] + erratum.updated = data['updated'] || data['issued'] + erratum.severity = data['severity'] || '' + erratum.solution = data['solution'] || '' + erratum.reboot_suggested = data['reboot_suggested'] || false + erratum.errata_type = 'security' + erratum.save! + data['cves']&.each do |cve| + erratum.cves.find_or_initialize_by(cve_id: cve) + end + data['dbts_bugs']&.each do |dbts_bug| + erratum.dbts_bugs.find_or_initialize_by(bug_id: dbts_bug) + end + # Check if the synced repository satisfies this erratum's package-requests + solution_pkgs_in_repo = [] + data['packages']&.each do |package| + solution_deb = erratum.deb_packages.find_or_initialize_by( + name: package['name'], + release: package['release'], + version: package['version'] + ) + solution_deb.save! + solution_pkgs_in_repo << solution_deb + end + # get all debs from the repo that have the same name + debs_erratum_in_repo = repo.debs.where(name: solution_pkgs_in_repo.map { |pkg| pkg.name }).distinct + # for these package(-names) check that all have a version bigger or equal than in the Erratum + debs_solving_erratum = repo.debs.solving_erratum_debs(solution_pkgs_in_repo) + # make sure all package-names available in repo are also in a version that resolves the Erratum + if debs_solving_erratum.pluck(:name).to_set == debs_erratum_in_repo.pluck(:name).to_set + erratum.repositories << repo unless erratum.repositories.include?(repo) + else + Rails.logger.warn("Erratum #{erratum.errata_id} not solvable by repo #{repo}, check you are synching the latest upstream-version of the repository!") + end + + erratum.save! + end + end + repo.root.update(deb_errata_url_etag: output[:etag]) + end + end + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + def humanized_output + output.dup.update(data: 'Trimmed') + end + + def rescue_strategy + Dynflow::Action::Rescue::Skip + end + end + end + end +end diff --git a/app/models/katello/concerns/host_managed_extensions.rb b/app/models/katello/concerns/host_managed_extensions.rb index 1bc376e9837..476867172ad 100644 --- a/app/models/katello/concerns/host_managed_extensions.rb +++ b/app/models/katello/concerns/host_managed_extensions.rb @@ -288,6 +288,19 @@ def rhsm_fact_values self.fact_values.joins(:fact_name).where("#{::FactName.table_name}.type = '#{Katello::RhsmFactName}'") end + def debs_for_erratum(erratum_name, only_upgradeable: false) + erratum = Katello::Erratum.find_by errata_id: erratum_name + return nil if erratum.nil? + pkgs = erratum.deb_packages.where(release: self.operatingsystem.release_name).pluck(:name) + + if only_upgradeable + installed = installed_debs.pluck(:name) + pkgs &= installed + end + + pkgs.join(' ') + end + def self.available_locks [:update] end @@ -625,7 +638,7 @@ class ::Host::Managed::Jail < Safemode::Jail :installed_packages, :traces_helpers, :advisory_ids, :package_names_for_job_template, :filtered_entitlement_quantity_consumed, :bound_repositories, :single_content_view, :single_lifecycle_environment, :purpose_role, :purpose_usage, :release_version, - :purpose_role_status_label, :purpose_usage_status_label + :purpose_role_status_label, :purpose_usage_status_label, :debs_for_erratum end class ActiveRecord::Associations::CollectionProxy::Jail < Safemode::Jail diff --git a/app/models/katello/deb.rb b/app/models/katello/deb.rb index 3a0e2b35404..3352ec90897 100644 --- a/app/models/katello/deb.rb +++ b/app/models/katello/deb.rb @@ -17,6 +17,16 @@ class Deb < Katello::Model scoped_search :on => :filename, :complete_value => true scoped_search :on => :checksum + # for given list/query of Katello::ErratumDebPackages in `erratum_deb_packages`, + # find all deb-packages with corresponding name and newer or equal version + # aka. find all debs that 'solve' the ErratumDebPackages + def self.solving_erratum_debs(erratum_deb_packages) + joins("INNER JOIN #{::Katello::ErratumDebPackage.table_name} edp" \ + " ON #{::Katello::Deb.table_name}.name = edp.name " \ + " AND deb_version_cmp(#{::Katello::Deb.table_name}.version, edp.version) >= 0") + .where('edp.id' => erratum_deb_packages).distinct + end + def self.default_sort order(:name).order(:version).order(:architecture) end diff --git a/app/models/katello/errata_status.rb b/app/models/katello/errata_status.rb index 1c0139db64c..d6dce7dfea4 100644 --- a/app/models/katello/errata_status.rb +++ b/app/models/katello/errata_status.rb @@ -62,7 +62,7 @@ def to_status(_options = {}) NEEDED_SECURITY_ERRATA elsif errata.any? NEEDED_ERRATA - elsif host.installed_packages.empty? || host.content_facet.bound_repositories.empty? + elsif (host.installed_packages.empty? && host.installed_debs.empty?) || host.content_facet.bound_repositories.empty? UNKNOWN else UP_TO_DATE diff --git a/app/models/katello/erratum.rb b/app/models/katello/erratum.rb index e9bf066f588..b9d21e91b1d 100644 --- a/app/models/katello/erratum.rb +++ b/app/models/katello/erratum.rb @@ -21,8 +21,10 @@ class Erratum < Katello::Model has_many :content_facets, :through => :content_facet_errata, :class_name => "Katello::Host::ContentFacet", :source => :content_facet has_many :content_facets_applicable, :through => :content_facet_errata, :class_name => "Katello::Host::ContentFacet", :source => :content_facet has_many :bugzillas, :class_name => "Katello::ErratumBugzilla", :dependent => :destroy, :inverse_of => :erratum + has_many :dbts_bugs, :class_name => "Katello::ErratumDbtsBug", :dependent => :destroy, :inverse_of => :erratum has_many :cves, :class_name => "Katello::ErratumCve", :dependent => :destroy, :inverse_of => :erratum has_many :packages, :class_name => "Katello::ErratumPackage", :dependent => :destroy, :inverse_of => :erratum + has_many :deb_packages, :class_name => "Katello::ErratumDebPackage", :dependent => :destroy, :inverse_of => :erratum scoped_search :on => :id, :rename => :db_id, :only_explicit => true, :validator => ScopedSearch::Validators::INTEGER scoped_search :on => :errata_id, :complete_value => true, :only_explicit => true @@ -37,8 +39,10 @@ class Erratum < Katello::Model scoped_search :on => :reboot_suggested, :complete_value => true scoped_search :relation => :cves, :on => :cve_id, :rename => :cve scoped_search :relation => :bugzillas, :on => :bug_id, :rename => :bug + scoped_search :relation => :dbts_bugs, :on => :bug_id, :rename => :bug scoped_search :relation => :packages, :on => :nvrea, :rename => :package, :complete_value => true, :only_explicit => true scoped_search :relation => :packages, :on => :name, :rename => :package_name, :complete_value => true, :only_explicit => true + scoped_search :relation => :deb_packages, :on => :name, :rename => :package_name, :complete_value => true, :only_explicit => true scoped_search :on => :modular, :only_explicit => true, @@ -180,6 +184,10 @@ def content_view_filters Katello::ContentViewErratumFilterRule.where(errata_id: self.errata_id).eager_load(:filter).map(&:filter) end + def all_package_names + package_names + deb_packages.map { |pkg| pkg.name } + end + apipie :class, desc: "A class representing #{model_name.human} object" do name 'Erratum' refs 'Erratum' @@ -196,7 +204,7 @@ def content_view_filters property :summary, String, desc: 'Returns the errata summary, the length can very, it is usually in range of 60 to 1000 characters. It can include empty line characters.' end class Jail < ::Safemode::Jail - allow :errata_id, :errata_type, :issued, :created_at, :severity, :package_names, :cves, :reboot_suggested, :title, :summary + allow :errata_id, :errata_type, :issued, :created_at, :severity, :package_names, :all_package_names, :cves, :reboot_suggested, :title, :summary end end end diff --git a/app/models/katello/erratum_dbts_bug.rb b/app/models/katello/erratum_dbts_bug.rb new file mode 100644 index 00000000000..16c737d8997 --- /dev/null +++ b/app/models/katello/erratum_dbts_bug.rb @@ -0,0 +1,9 @@ +module Katello + class ErratumDbtsBug < Katello::Model + belongs_to :erratum, inverse_of: :dbts_bugs, class_name: 'Katello::Erratum' + + def href + "https://bugs.debian.org/#{bug_id}" + end + end +end diff --git a/app/models/katello/erratum_deb_package.rb b/app/models/katello/erratum_deb_package.rb new file mode 100644 index 00000000000..a0f818c8a4d --- /dev/null +++ b/app/models/katello/erratum_deb_package.rb @@ -0,0 +1,5 @@ +module Katello + class ErratumDebPackage < Katello::Model + belongs_to :erratum, :inverse_of => :deb_packages, :class_name => 'Katello::Erratum' + end +end diff --git a/app/models/katello/repository.rb b/app/models/katello/repository.rb index 567006d65f6..c2ab6efe68c 100644 --- a/app/models/katello/repository.rb +++ b/app/models/katello/repository.rb @@ -337,6 +337,9 @@ def content_counts content_counts[content_type.content_type] = content_type&.model_class&.in_repositories(self)&.where(:content_type => content_type.content_type)&.count else content_counts[content_type.label] = content_type&.model_class&.in_repositories(self)&.count + if content_type&.model_class::CONTENT_TYPE == Deb::CONTENT_TYPE + content_counts['erratum'] = self.errata.count + end end end diff --git a/app/models/katello/root_repository.rb b/app/models/katello/root_repository.rb index 6b6689a404e..4fdfa1bc44f 100644 --- a/app/models/katello/root_repository.rb +++ b/app/models/katello/root_repository.rb @@ -134,6 +134,13 @@ class RootRepository < Katello::Model scope :custom, -> { where.not(:id => self.redhat) } delegate :redhat?, :provider, :organization, to: :product delegate :cdn_configuration, to: :organization + before_save :reset_deb_errata_url_etag + + def reset_deb_errata_url_etag + if self.deb? && !self.changed.include?('deb_errata_url_etag') + self.deb_errata_url_etag = nil + end + end def library_instance repositories.in_default_view.first @@ -409,6 +416,14 @@ def pulp_update_needed? changeable_attributes.any? { |key| previous_changes.key?(key) } end + def supports_errata? + if deb? + self.deb_errata_url.present? + else + self.repository_type.supports_content_type Katello::Erratum + end + end + def raw_content_path self.content.content_url end diff --git a/app/services/katello/applicability/applicable_content_helper.rb b/app/services/katello/applicability/applicable_content_helper.rb index 53f2b3d7461..baf7a8c66ca 100644 --- a/app/services/katello/applicability/applicable_content_helper.rb +++ b/app/services/katello/applicability/applicable_content_helper.rb @@ -21,7 +21,7 @@ def calculate_and_import def fetch_content_ids if self.content_unit_class == ::Katello::Erratum - fetch_errata_content_ids + fetch_errata_content_ids + fetch_deb_errata_content_ids elsif self.content_unit_class == ::Katello::Deb fetch_deb_content_ids elsif self.content_unit_class == ::Katello::ModuleStream @@ -31,6 +31,16 @@ def fetch_content_ids end end + def fetch_deb_errata_content_ids + Katello::Erratum.joins([:deb_packages, :repositories], + "INNER JOIN #{Katello::Deb.table_name} ON #{Katello::ErratumDebPackage.table_name}.name = #{Katello::Deb.table_name}.name", + "INNER JOIN #{Katello::ContentFacetApplicableDeb.table_name} ON #{Katello::ContentFacetApplicableDeb.table_name}.deb_id = #{Katello::Deb.table_name}.id") + .where("deb_version_cmp(#{Katello::ErratumDebPackage.table_name}.version, #{Katello::Deb.table_name}.version) >= 0") + .where("#{Katello::ContentFacetApplicableDeb.table_name}.content_facet_id": content_facet.id) + .where("#{Katello::RepositoryErratum.table_name}.repository_id" => self.bound_library_instance_repos) + .distinct.pluck(:id) + end + def fetch_errata_content_ids # Query for all Errata ids that are attached to the host's applicable packages query = 'SELECT DISTINCT katello_repository_errata.erratum_id AS id FROM katello_repository_errata diff --git a/app/services/katello/repository_type.rb b/app/services/katello/repository_type.rb index 743c4940514..1ed845e666c 100644 --- a/app/services/katello/repository_type.rb +++ b/app/services/katello/repository_type.rb @@ -75,6 +75,13 @@ def content_types_to_index @content_types.select { |type| type.index } end + def supports_content_type(model_class) + @content_types.each do |type| + return true if type.model_class == model_class + end + false + end + def default_managed_content_type(label = nil) if label @default_managed_content_type_label = label.to_s diff --git a/app/views/foreman/job_templates/install_errata.erb b/app/views/foreman/job_templates/install_errata.erb index 83d22df9cef..84a1004f822 100644 --- a/app/views/foreman/job_templates/install_errata.erb +++ b/app/views/foreman/job_templates/install_errata.erb @@ -17,6 +17,9 @@ foreign_input_sets: <% if @host.operatingsystem.family == 'Suse' -%> <% advisories = input(:errata).split(',').join(' ') -%> <%= render_template('Package Action - Script Default', :action => 'install -n -t patch', :package => advisories) %> +<% elsif @host.operatingsystem.family == 'Debian' -%> + <% advisories = input(:errata).split(',').map { |e| @host.debs_for_erratum(e, only_upgradeable: true) }.join(' ') -%> + <%= render_template('Package Action - Script Default', :action => 'update', :package => advisories) %> <% else -%> <% advisories = input(:errata).split(',').map { |e| "--advisory=#{e}" }.join(' ') -%> <%= render_template('Package Action - Script Default', :action => 'update-minimal', :package => advisories) %> diff --git a/app/views/foreman/job_templates/install_errata_-_katello_ansible_default.erb b/app/views/foreman/job_templates/install_errata_-_katello_ansible_default.erb index 78d34494d55..2aaf3678fba 100644 --- a/app/views/foreman/job_templates/install_errata_-_katello_ansible_default.erb +++ b/app/views/foreman/job_templates/install_errata_-_katello_ansible_default.erb @@ -17,6 +17,9 @@ kind: job_template <% if @host.operatingsystem.family == 'Suse' -%> <% advisories = input(:errata).split(',').join(' ') -%> <%= render_template('Run Command - Ansible Default', :command => "zypper -n install -t patch #{advisories}") %> +<% elsif @host.operatingsystem.family == 'Debian' -%> +<% advisories = input(:errata).split(',').map { |e| @host.debs_for_erratum(e, only_upgradeable: true) }.join(' ') -%> +<%= render_template('Run Command - Ansible Default', :command => "apt-get -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -y --only-upgrade install -y #{advisories}") %> <% else -%> <% advisories = input(:errata).split(',').map { |e| "--advisory=#{e}" }.join(' ') -%> <%= render_template('Run Command - Ansible Default', :command => "yum -y update-minimal #{advisories}") %> diff --git a/app/views/foreman/job_templates/install_errata_by_search_query.erb b/app/views/foreman/job_templates/install_errata_by_search_query.erb index 3335c6eea3a..00475a92dda 100644 --- a/app/views/foreman/job_templates/install_errata_by_search_query.erb +++ b/app/views/foreman/job_templates/install_errata_by_search_query.erb @@ -21,6 +21,9 @@ foreign_input_sets: <% if @host.operatingsystem.family == 'Suse' -%> <%= render_template('Package Action - Script Default', :action => 'install -n -t patch', :package => advisory_ids.join(' ')) %> +<% elsif @host.operatingsystem.family == 'Debian' -%> + <% pkgs = advisory_ids.map { |e| @host.debs_for_erratum(e, only_upgradeable: true) }.join(' ') -%> + <%= render_template('Package Action - Script Default', :action => 'update', :package => pkgs) %> <% else -%> <% advisories = advisory_ids.map { |e| "--advisory=#{e}" }.join(' ') -%> <%= render_template('Package Action - Script Default', :action => 'update-minimal', :package => advisories) %> diff --git a/app/views/katello/api/v2/errata/show.json.rabl b/app/views/katello/api/v2/errata/show.json.rabl index 539794b2206..66a9de05ef4 100644 --- a/app/views/katello/api/v2/errata/show.json.rabl +++ b/app/views/katello/api/v2/errata/show.json.rabl @@ -29,3 +29,7 @@ end node :module_streams do |e| e.module_streams end + +child :deb_packages => :deb_packages do + attributes :name, :version, :release +end diff --git a/app/views/katello/api/v2/repositories/base.json.rabl b/app/views/katello/api/v2/repositories/base.json.rabl index 02a0c7bad52..0fce00eef53 100644 --- a/app/views/katello/api/v2/repositories/base.json.rabl +++ b/app/views/katello/api/v2/repositories/base.json.rabl @@ -11,6 +11,7 @@ attributes :mirroring_policy glue(@object.root) do attributes :content_type, :url, :arch, :os_versions, :content_id, :generic_remote_options attributes :major, :minor + attributes :supports_errata? => :supports_errata child :product do |_product| attributes :id, :cp_id, :name diff --git a/app/views/katello/api/v2/repositories/show.json.rabl b/app/views/katello/api/v2/repositories/show.json.rabl index 0fcb612afc2..89473fe3b1b 100644 --- a/app/views/katello/api/v2/repositories/show.json.rabl +++ b/app/views/katello/api/v2/repositories/show.json.rabl @@ -23,6 +23,7 @@ glue(@resource.root) do attributes :product_type attributes :upstream_username attributes :deb_releases, :deb_components, :deb_architectures + attributes :deb_errata_url attributes :http_proxy_policy attributes :http_proxy_id attributes :http_proxy_name diff --git a/db/migrate/20180406112559_create_katello_erratum_dbts_bugs.rb b/db/migrate/20180406112559_create_katello_erratum_dbts_bugs.rb new file mode 100644 index 00000000000..dc4c117a22e --- /dev/null +++ b/db/migrate/20180406112559_create_katello_erratum_dbts_bugs.rb @@ -0,0 +1,10 @@ +class CreateKatelloErratumDbtsBugs < ActiveRecord::Migration[4.2] + def change + create_table :katello_erratum_dbts_bugs do |t| + t.references :erratum + t.string :bug_id, limit: 255 + + t.timestamps + end + end +end diff --git a/db/migrate/20180406112600_create_katello_erratum_deb_packages.rb b/db/migrate/20180406112600_create_katello_erratum_deb_packages.rb new file mode 100644 index 00000000000..c45714d61dc --- /dev/null +++ b/db/migrate/20180406112600_create_katello_erratum_deb_packages.rb @@ -0,0 +1,13 @@ +class CreateKatelloErratumDebPackages < ActiveRecord::Migration[4.2] + def change + create_table :katello_erratum_deb_packages do |t| + t.references :erratum + t.string :name, limit: 255 + t.string :version, limit: 255 + t.string :filename, limit: 255 + t.string :release, limit: 255 + + t.timestamps + end + end +end diff --git a/db/migrate/20190116131820_add_deb_errata_url_to_root_repositories.rb b/db/migrate/20190116131820_add_deb_errata_url_to_root_repositories.rb new file mode 100644 index 00000000000..6e066607016 --- /dev/null +++ b/db/migrate/20190116131820_add_deb_errata_url_to_root_repositories.rb @@ -0,0 +1,6 @@ +class AddDebErrataUrlToRootRepositories < ActiveRecord::Migration[5.1] + def change + add_column :katello_root_repositories, :deb_errata_url, :string, limit: 255 + add_column :katello_root_repositories, :deb_errata_url_etag, :string, limit: 255 + end +end diff --git a/engines/bastion/grunt/karma.js b/engines/bastion/grunt/karma.js index 12030578525..4cd1fcb9cfc 100644 --- a/engines/bastion/grunt/karma.js +++ b/engines/bastion/grunt/karma.js @@ -60,6 +60,7 @@ module.exports = { 'app/assets/javascripts/' + pluginName + '/**/*.js', 'app/assets/javascripts/' + pluginName + '/**/*.html', + basePath + '../babel-polyfill/dist/polyfill.js', basePath + 'test/test-mocks.module.js', 'test/**/*test.js' ], diff --git a/engines/bastion/package.json b/engines/bastion/package.json index fcf04b893fb..ac281e85376 100644 --- a/engines/bastion/package.json +++ b/engines/bastion/package.json @@ -2,6 +2,7 @@ "name": "bastion", "version": "0.1.0", "dependencies": { + "babel-polyfill": "^6.26.0", "eslint": "~6.6.0", "eslint-plugin-angular": "0.0.3", "generator-bastion": "~0.1.2", diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/errata-details.html b/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/errata-details.html index aa34bbb6405..eef7de11d56 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/errata-details.html +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/errata-details.html @@ -38,6 +38,9 @@

{{ erratum.errata_id }}

  • {{ package }}
  • +
  • + {{ deb_package.name }} {{ deb_package.version }} +
  • diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/erratum.controller.js b/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/erratum.controller.js index bea44dcf4af..c68aebec128 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/erratum.controller.js +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/erratum.controller.js @@ -12,6 +12,8 @@ */ angular.module('Bastion.errata').controller('ErratumController', ['$scope', 'Erratum', 'ApiErrorHandler', 'translate', function ($scope, Erratum, ApiErrorHandler, translate) { + $scope.encodeURIComponent = encodeURIComponent; + $scope.panel = { error: false, loading: true diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/views/erratum-packages.html b/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/views/erratum-packages.html index d3442b143c8..1d5d46d8ab4 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/views/erratum-packages.html +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/details/views/erratum-packages.html @@ -4,12 +4,18 @@

    Independent Packages

    @@ -18,7 +24,7 @@

    Module Stream Packages