diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml
index 9edd0cb76f4..ea468284289 100644
--- a/.github/workflows/ruby.yml
+++ b/.github/workflows/ruby.yml
@@ -31,7 +31,7 @@ jobs:
angular:
name: Angular ${{ matrix.engine }} - NodeJS ${{ matrix.node }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
diff --git a/.gitignore b/.gitignore
index 63493844a8c..b418ed9d5b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ locale/*/*.po.time_stamp
locale/.cache/*
locale/*/*.pox
locale/*/LC_MESSAGES
+locale/action_names.rb
.yardoc
yardoc
doc/apidoc*
diff --git a/app/controllers/katello/api/registry/registry_proxies_controller.rb b/app/controllers/katello/api/registry/registry_proxies_controller.rb
index f224f725e49..965ca134a8a 100644
--- a/app/controllers/katello/api/registry/registry_proxies_controller.rb
+++ b/app/controllers/katello/api/registry/registry_proxies_controller.rb
@@ -5,7 +5,7 @@ class Api::Registry::RegistryProxiesController < Api::V2::ApiController
before_action :confirm_settings
skip_before_action :authorize
before_action :optional_authorize, only: [:token, :catalog]
- before_action :registry_authorize, except: [:token, :v1_search, :catalog]
+ before_action :registry_authorize, except: [:token, :v1_search, :catalog, :static_index]
before_action :authorize_repository_read, only: [:pull_manifest, :tags_list, :check_blob, :pull_blob]
before_action :container_push_prop_validation, only: [:start_upload_blob, :upload_blob, :finish_upload_blob, :push_manifest]
before_action :create_container_repo_if_needed, only: [:start_upload_blob, :upload_blob, :finish_upload_blob, :push_manifest]
@@ -806,5 +806,25 @@ def render_podman_error(code, message, status = :bad_request)
def item_not_found(item)
render_podman_error("NAME_UNKNOWN", _("%s was not found!") % item, :not_found)
end
+
+ def static_index
+ host_ip = request.remote_ip
+ host = ::Host.joins(:primary_interface).where("nics.ip = :host_ip OR nics.ip6 = :host_ip", host_ip: host_ip)&.first
+ flatpak_index = (redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers) })
+ flatpak_index_json = JSON.parse(flatpak_index)
+ # Filter out repositories if it's a registered host
+ if host&.content_view_environments&.any?
+ # host.update(flatpak_index: flatpak_index) Will this help??
+ repos = host.content_view_environments.flat_map do |cve|
+ cve.content_view_version.repositories
+ end
+ available_container_repo_names = repos.map(&:container_repository_name)
+ flatpak_index_json['Results'] = flatpak_index_json['Results'].select do |result|
+ available_container_repo_names.include?(result['Name'])
+ end
+ end
+ # Otherwise just return unfiltered pulp flatpak index
+ render json: flatpak_index_json
+ end
end
end
diff --git a/app/controllers/katello/api/v2/content_uploads_controller.rb b/app/controllers/katello/api/v2/content_uploads_controller.rb
index d28e457c7ce..024f43bca7f 100644
--- a/app/controllers/katello/api/v2/content_uploads_controller.rb
+++ b/app/controllers/katello/api/v2/content_uploads_controller.rb
@@ -12,9 +12,10 @@ class Api::V2::ContentUploadsController < Api::V2::ApiController
param :repository_id, :number, :required => true, :desc => N_("repository id")
param :size, :number, :required => true, :desc => N_("Size of file to upload")
param :checksum, String, :required => false, :desc => N_("Checksum of file to upload")
- param :content_type, RepositoryTypeManager.uploadable_content_types(false).map(&:label), :required => false, :desc => N_("content type ('deb', 'docker_manifest', 'file', 'ostree_ref', 'rpm', 'srpm')")
+ param :content_type, RepositoryTypeManager.uploadable_content_types(false).map(&:label), :required => false, :desc => N_("content type ('deb', 'file', 'ostree_ref', 'rpm', 'srpm')")
def create
fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Ansible collections.") if @repository.ansible_collection?
+ fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload container content via Hammer/API. Use podman push instead.") if @repository.docker?
content_type = params[:content_type] || ::Katello::RepositoryTypeManager.find(@repository.content_type)&.default_managed_content_type&.label
RepositoryTypeManager.check_content_matches_repo_type!(@repository, content_type)
if ::Katello::RepositoryTypeManager.generic_content_type?(content_type)
diff --git a/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb b/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
index 0b61607da58..5054a94e332 100644
--- a/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
+++ b/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
@@ -40,6 +40,7 @@ def resource_class
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
param :date_type, String, :desc => N_("erratum: search using the 'Issued On' or 'Updated On' column of the errata. Values are 'issued'/'updated'")
param :module_stream_ids, Array, :desc => N_("module stream ids")
+ param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
def create
rule_clazz = ContentViewFilter.rule_class_for(@filter)
@@ -89,6 +90,7 @@ def show
param :start_date, String, :desc => N_("erratum: start date (YYYY-MM-DD)")
param :end_date, String, :desc => N_("erratum: end date (YYYY-MM-DD)")
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
+ param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
def update
update_params = rule_params
update_params[:name] = update_params[:name].first if update_params[:name]
@@ -136,7 +138,7 @@ def rule_params
@rule_params ||= params.fetch(:content_view_filter_rule, {}).
permit(:uuid, :version, :min_version, :max_version, :architecture,
- :errata_id, :start_date, :end_date, :date_type,
+ :errata_id, :start_date, :end_date, :date_type, :allow_other_types,
:types => [], :module_stream_ids => [], :errata_ids => [], name: [])
end
diff --git a/app/controllers/katello/api/v2/content_views_controller.rb b/app/controllers/katello/api/v2/content_views_controller.rb
index a182c1eaa48..197ab139c59 100644
--- a/app/controllers/katello/api/v2/content_views_controller.rb
+++ b/app/controllers/katello/api/v2/content_views_controller.rb
@@ -288,7 +288,10 @@ def view_params
if (!@content_view || !@content_view.composite?)
attrs.push({:repository_ids => []}, :repository_ids)
end
- params.require(:content_view).permit(*attrs).to_h
+ result = params.require(:content_view).permit(*attrs).to_h
+ # sanitize repository_ids to be a list of integers
+ result[:repository_ids] = result[:repository_ids].compact.map(&:to_i) if result[:repository_ids].present?
+ result
end
def find_environment
diff --git a/app/controllers/katello/api/v2/environments_controller.rb b/app/controllers/katello/api/v2/environments_controller.rb
index 764a4aa5810..a0e70be7067 100644
--- a/app/controllers/katello/api/v2/environments_controller.rb
+++ b/app/controllers/katello/api/v2/environments_controller.rb
@@ -84,6 +84,10 @@ def show
ID of an environment that is prior to the new environment in the chain. It has to be
either the ID of Library or the ID of an environment at the end of a chain.
DESC
+ param :path_id, Integer, :desc => <<-DESC
+ If you are adding an environment to an existing path after Library, pass the ID of the environment that is the current successor of Library in the path.
+ It has to be the id of the old environment following library in this path.
+ DESC
def create
create_params = environment_params
create_params[:label] = labelize_params(create_params)
diff --git a/app/controllers/katello/api/v2/errata_controller.rb b/app/controllers/katello/api/v2/errata_controller.rb
index d8040aea0b8..57f113c4182 100644
--- a/app/controllers/katello/api/v2/errata_controller.rb
+++ b/app/controllers/katello/api/v2/errata_controller.rb
@@ -59,9 +59,20 @@ def custom_index_relation(collection)
fail HttpErrors::UnprocessableEntity, msg
end
+ custom_index_relation_handle_type_and_time(collection)
+ end
+
+ def custom_index_relation_handle_type_and_time(collection)
collection = collection.where("#{date_type} >= ?", params[:start_date]) if params[:start_date]
collection = collection.where("#{date_type} <= ?", params[:end_date]) if params[:end_date]
- collection = collection.of_type(params[:types]) if params[:types]
+ if params[:types]
+ include_other = params[:types]&.include?('other')
+ params[:types]&.delete('other')
+ collection = collection.of_type(
+ params[:types],
+ include_other
+ )
+ end
collection
end
diff --git a/app/controllers/katello/api/v2/host_bootc_images_controller.rb b/app/controllers/katello/api/v2/host_bootc_images_controller.rb
new file mode 100644
index 00000000000..d3a11566502
--- /dev/null
+++ b/app/controllers/katello/api/v2/host_bootc_images_controller.rb
@@ -0,0 +1,71 @@
+module Katello
+ class Api::V2::HostBootcImagesController < Api::V2::ApiController
+ include Katello::Concerns::FilteredAutoCompleteSearch
+
+ resource_description do
+ api_version 'v2'
+ api_base_url "/api"
+ end
+
+ api :GET, "/hosts/bootc_images", N_("List booted bootc container images for hosts")
+ param_group :search, Api::V2::ApiController
+ def bootc_images
+ params[:sort_by] ||= 'bootc_booted_image'
+ params[:sort_order] ||= 'asc'
+ if params[:order]
+ params[:order] = "#{params[:order].split(' ')[0]} #{sanitize_sort_order(params[:order].split(' ')[1])}"
+ else
+ params[:order] = "#{params[:sort_by]} #{sanitize_sort_order(params[:sort_order])}"
+ end
+ per_page = params[:per_page].present? ? params[:per_page].to_i : Setting[:entries_per_page]
+ page = params[:page].present? ? params[:page].to_i : 1
+
+ bootc_image_map = bootc_host_image_map
+ paged_images = bootc_image_map.to_a.paginate(page: page, per_page: per_page)
+ results = paged_images.collect { |image| { bootc_booted_image: image[0], digests: image[1] } }
+ render json: {
+ total: bootc_image_map.size,
+ page: page,
+ per_page: per_page,
+ subtotal: bootc_image_map.size,
+ sort: {
+ by: params[:sort_by],
+ order: params[:sort_order],
+ },
+ results: results,
+ }
+ end
+
+ private
+
+ def sanitize_sort_order(sort_order)
+ if sort_order.present? && ['asc', 'desc'].include?(sort_order.downcase)
+ sort_order.downcase
+ else
+ 'asc'
+ end
+ end
+
+ def index_relation
+ query = resource_class.authorized(:view_hosts).distinct
+ query.joins(:content_facet).where.not(bootc_booted_image: nil, bootc_booted_digest: nil)
+ query
+ end
+
+ def resource_class
+ ::Host::Managed
+ end
+
+ def bootc_host_image_map
+ content_facets = ::Katello::Host::ContentFacet.where(host_id: ::Host::Managed.joins(:content_facet).search_for(params[:search]).pluck(:id))
+ aggregate_bootc_data = content_facets.where.not(bootc_booted_image: nil, bootc_booted_digest: nil).
+ select(:bootc_booted_image, :bootc_booted_digest, 'COUNT(hosts.id) as host_count').
+ joins(:host).group(:bootc_booted_image, :bootc_booted_digest).order(params[:order])
+ bootc_image_map = Hash.new { |h, k| h[k] = [] }
+ aggregate_bootc_data.each do |host_image|
+ bootc_image_map[host_image.bootc_booted_image] << { bootc_booted_digest: host_image.bootc_booted_digest, host_count: host_image.host_count.to_i }
+ end
+ bootc_image_map
+ end
+ end
+end
diff --git a/app/controllers/katello/api/v2/host_subscriptions_controller.rb b/app/controllers/katello/api/v2/host_subscriptions_controller.rb
index 865dac810e1..04400d68afa 100644
--- a/app/controllers/katello/api/v2/host_subscriptions_controller.rb
+++ b/app/controllers/katello/api/v2/host_subscriptions_controller.rb
@@ -184,7 +184,7 @@ def content_override
validate_content_overrides_enabled(override_params)
end
sync_task(::Actions::Katello::Host::UpdateContentOverrides, @host, content_override_values, false)
- fetch_product_content
+ fetch_product_content(!params.dig(:content_overrides_search, :search).nil? && Foreman::Cast.to_bool(params.dig(:content_overrides_search, :limit_to_env)))
end
api :GET, "/hosts/:host_id/subscriptions/available_release_versions", N_("Show releases available for the content host")
@@ -203,8 +203,8 @@ def enabled_repositories
private
- def fetch_product_content
- content_finder = ProductContentFinder.new(:consumable => @host.subscription_facet)
+ def fetch_product_content(limit_to_env = false)
+ content_finder = ProductContentFinder.new(:match_environment => limit_to_env, :consumable => @host.subscription_facet)
content = content_finder.presenter_with_overrides(@host.subscription_facet.candlepin_consumer.content_overrides)
respond_with_template_collection("index", 'repository_sets', :collection => full_result_response(content))
end
diff --git a/app/controllers/katello/concerns/api/v2/http_proxies_controller_extensions.rb b/app/controllers/katello/concerns/api/v2/http_proxies_controller_extensions.rb
index 2d2f8c5baca..2839a6a82f9 100644
--- a/app/controllers/katello/concerns/api/v2/http_proxies_controller_extensions.rb
+++ b/app/controllers/katello/concerns/api/v2/http_proxies_controller_extensions.rb
@@ -5,9 +5,9 @@ module V2
module HttpProxiesControllerExtensions
extend ::Apipie::DSL::Concern
- update_api(:create) do
+ update_api(:create, :show) do
param :http_proxy, Hash do
- param :default_content_proxy, :bool, :required => false, :desc => N_('Set this HTTP proxy as the default content HTTP proxy')
+ param :content_default_http_proxy, :bool, :required => false, :desc => N_('Set this HTTP proxy as the default content HTTP proxy')
end
end
end
diff --git a/app/controllers/katello/concerns/http_proxies_controller_extensions.rb b/app/controllers/katello/concerns/http_proxies_controller_extensions.rb
index 303a9e11c8e..00049d9282c 100644
--- a/app/controllers/katello/concerns/http_proxies_controller_extensions.rb
+++ b/app/controllers/katello/concerns/http_proxies_controller_extensions.rb
@@ -11,7 +11,7 @@ module HttpProxiesControllerExtensions
def update_content_default_http_proxy
return unless @http_proxy.persisted?
- return unless ActiveRecord::Type::Boolean.new.deserialize(params.dig('http_proxy', 'default_content'))
+ return unless ActiveRecord::Type::Boolean.new.deserialize(params.dig('http_proxy', 'content_default_http_proxy'))
Setting[:content_default_http_proxy] = @http_proxy.name
end
diff --git a/app/lib/actions/katello/content_view_environment/reassign_objects.rb b/app/lib/actions/katello/content_view_environment/reassign_objects.rb
index ac903337a0e..05ad9ab1350 100644
--- a/app/lib/actions/katello/content_view_environment/reassign_objects.rb
+++ b/app/lib/actions/katello/content_view_environment/reassign_objects.rb
@@ -14,7 +14,11 @@ def plan(content_view_environment, options)
end
content_view_environment.activation_keys.each do |key|
- plan_action(ActivationKey::Reassign, key, options[:key_content_view_id], options[:key_environment_id])
+ if key.multi_content_view_environment?
+ key.content_view_environments = key.content_view_environments - [content_view_environment]
+ else
+ plan_action(ActivationKey::Reassign, key, options[:key_content_view_id], options[:key_environment_id])
+ end
end
end
end
diff --git a/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb b/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
index 3eee2a8f252..7b512488c6d 100644
--- a/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
+++ b/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
@@ -2,9 +2,9 @@ module Katello
module Validators
class ContentViewErratumFilterRuleValidator < ActiveModel::Validator
def validate(record)
- if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank?
+ if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank? && record.allow_other_types == false
invalid_parameters = _("Invalid erratum filter rule specified, Must specify at least one of the following:" \
- " 'errata_id', 'start_date', 'end_date' or 'types'")
+ " 'errata_id', 'start_date', 'end_date', 'types', or 'allow_other_types'")
record.errors.add(:base, invalid_parameters)
return
end
diff --git a/app/models/katello/activation_key.rb b/app/models/katello/activation_key.rb
index 571f2e57b2d..4666df4d71d 100644
--- a/app/models/katello/activation_key.rb
+++ b/app/models/katello/activation_key.rb
@@ -92,7 +92,7 @@ def self.in_environments(envs)
def content_view_environments=(new_cves)
if new_cves.length > 1 && !Setting['allow_multiple_content_views']
fail ::Katello::Errors::MultiEnvironmentNotSupportedError,
- _("Assigning an activation key to multiple content view environments is not enabled.")
+ _("Assigning an activation key to multiple content view environments is not enabled. To enable, set the allow_multiple_content_views setting.")
end
super(new_cves)
Katello::ContentViewEnvironmentActivationKey.reprioritize_for_activation_key(self, new_cves)
diff --git a/app/models/katello/concerns/http_proxy_extensions.rb b/app/models/katello/concerns/http_proxy_extensions.rb
index ad2ef8d9aa7..d12940d0ec3 100644
--- a/app/models/katello/concerns/http_proxy_extensions.rb
+++ b/app/models/katello/concerns/http_proxy_extensions.rb
@@ -84,6 +84,10 @@ def name_and_url
uri.user = nil
"#{name} (#{uri})"
end
+
+ def content_default_http_proxy?
+ Setting[:content_default_http_proxy] == name
+ end
end
end
end
diff --git a/app/models/katello/concerns/smart_proxy_extensions.rb b/app/models/katello/concerns/smart_proxy_extensions.rb
index 3761f4110e6..abbbc998191 100644
--- a/app/models/katello/concerns/smart_proxy_extensions.rb
+++ b/app/models/katello/concerns/smart_proxy_extensions.rb
@@ -33,6 +33,7 @@ def refresh
before_create :associate_default_locations
before_create :associate_lifecycle_environments
before_validation :set_default_download_policy
+ after_update :refresh_smart_proxy_sync_histories
lazy_accessor :pulp_repositories, :initializer => lambda { |_s| pulp_node.extensions.repository.retrieve_all }
@@ -471,6 +472,9 @@ def add_lifecycle_environment(environment)
end
def remove_lifecycle_environment(environment)
+ smart_proxy_helper = ::Katello::SmartProxyHelper.new(self)
+ repos = smart_proxy_helper.repositories_available_to_capsule(environment)
+ smart_proxy_helper.clear_smart_proxy_sync_histories(repos) unless repos.empty?
self.lifecycle_environments.find(environment.id)
unless self.lifecycle_environments.destroy(environment)
fail _("Could not remove the lifecycle environment from the smart proxy")
@@ -479,6 +483,16 @@ def remove_lifecycle_environment(environment)
raise _("Lifecycle environment was not attached to the smart proxy; therefore, no changes were made.")
end
+ def refresh_smart_proxy_sync_histories
+ smart_proxy_helper = ::Katello::SmartProxyHelper.new(self)
+ repos = smart_proxy_helper.repositories_available_to_capsule.select(:id)
+ if repos.size == 0
+ self.smart_proxy_sync_histories.delete_all
+ else
+ self.smart_proxy_sync_histories.where.not(repository_id: repos).delete_all
+ end
+ end
+
def available_lifecycle_environments(organization_id = nil)
scope = Katello::KTEnvironment.not_in_capsule(self)
scope = scope.where(organization_id: organization_id) if organization_id
diff --git a/app/models/katello/content_view_erratum_filter.rb b/app/models/katello/content_view_erratum_filter.rb
index ee8d9f98103..a6e39a65c19 100644
--- a/app/models/katello/content_view_erratum_filter.rb
+++ b/app/models/katello/content_view_erratum_filter.rb
@@ -92,9 +92,21 @@ def erratum_arel
end
def types_clause
+ # Create an array to store output clauses for quick type filtering later
+ conditions = []
+
+ # Add clauses for types in the filter
types = erratum_rules.first.types
- return if types.blank?
- errata_types_in(types)
+ conditions << errata_types_in(types) unless types.blank?
+
+ # Add clauses for 'other' types
+ conditions << errata_types_not_in(Erratum::TYPES) if erratum_rules.first.allow_other_types?
+
+ # Reduce the array of clauses to a single clause and return
+ return if conditions.empty?
+ conditions.reduce(nil) do |combined_clause, condition|
+ combined_clause ? combined_clause.or(condition) : condition
+ end
end
def filter_by_id?
@@ -105,6 +117,10 @@ def errata_types_in(types)
erratum_arel[:errata_type].in(types)
end
+ def errata_types_not_in(types)
+ erratum_arel[:errata_type].not_in(types)
+ end
+
def errata_in(ids)
erratum_arel[:errata_id].in(ids)
end
diff --git a/app/models/katello/erratum.rb b/app/models/katello/erratum.rb
index b68062ee1cd..9a6b3893d51 100644
--- a/app/models/katello/erratum.rb
+++ b/app/models/katello/erratum.rb
@@ -48,8 +48,14 @@ class Erratum < Katello::Model
:validator => ->(value) { ['true', 'false'].include?(value.downcase) },
:operators => ["="]
- def self.of_type(type)
- where(:errata_type => type)
+ def self.of_type(type, include_other = false)
+ if include_other
+ where.not(
+ :errata_type => [Erratum::SECURITY, Erratum::BUGZILLA, Erratum::ENHANCEMENT].flatten
+ ).or(where(:errata_type => type))
+ else
+ where(:errata_type => type)
+ end
end
scope :security, -> { of_type(Erratum::SECURITY) }
diff --git a/app/models/katello/host/content_facet.rb b/app/models/katello/host/content_facet.rb
index ae25d2fd1e9..353c010c2a7 100644
--- a/app/models/katello/host/content_facet.rb
+++ b/app/models/katello/host/content_facet.rb
@@ -59,7 +59,7 @@ class ContentFacet < Katello::Model
validates_associated :content_view_environment_content_facets, :message => _("invalid: The content source must sync the lifecycle environment assigned to the host. See the logs for more information.")
validates :host, :presence => true, :allow_blank => false
validates :bootc_booted_digest, :bootc_available_digest, :bootc_staged_digest, :bootc_rollback_digest,
- format: { with: /\Asha256:[A-Fa-f0-9]{64}\z/, message: "must be a valid sha256 digest", allow_nil: true }
+ format: { with: /\Asha256:[A-Fa-f0-9]{64}\z/, message: "must be a valid sha256 digest", allow_blank: true }
scope :with_environments, ->(lifecycle_environments) do
joins(:content_view_environment_content_facets => :content_view_environment).
@@ -136,7 +136,7 @@ def single_lifecycle_environment
def content_view_environments=(new_cves)
if new_cves.length > 1 && !Setting['allow_multiple_content_views']
fail ::Katello::Errors::MultiEnvironmentNotSupportedError,
- _("Assigning a host to multiple content view environments is not enabled.")
+ _("Assigning a host to multiple content view environments is not enabled. To enable, set the allow_multiple_content_views setting.")
end
super(new_cves)
Katello::ContentViewEnvironmentContentFacet.reprioritize_for_content_facet(self, new_cves)
diff --git a/app/models/katello/repository.rb b/app/models/katello/repository.rb
index 3abccdeb136..f1daefb7681 100644
--- a/app/models/katello/repository.rb
+++ b/app/models/katello/repository.rb
@@ -1059,10 +1059,15 @@ def deb_pulp_components(version_href = self.version_href)
pulp_api.list({:repository_version => version_href}).results.map { |x| x.component }.uniq
end
+ def deb_sanitize_pulp_distribution(distribution)
+ return "flat-repo" if distribution&.end_with?("/")
+ distribution
+ end
+
def deb_pulp_distributions(version_href = self.version_href)
return [] if version_href.blank?
pulp_api = Katello::Pulp3::Repository.instance_for_type(self, SmartProxy.pulp_primary).api.content_release_components_api
- pulp_api.list({:repository_version => version_href}).results.map { |x| x.distribution }.uniq
+ pulp_api.list({:repository_version => version_href}).results.map { |x| deb_sanitize_pulp_distribution(x.distribution) }.uniq
end
def sync_status
diff --git a/app/services/katello/pulp3/repository/apt.rb b/app/services/katello/pulp3/repository/apt.rb
index 2673e5209a0..9a5ded6c651 100644
--- a/app/services/katello/pulp3/repository/apt.rb
+++ b/app/services/katello/pulp3/repository/apt.rb
@@ -226,23 +226,6 @@ def copy_content_from_mapping(repo_id_map, _options = {})
multi_copy_units(repo_id_map, dependency_solving)
end
- def copy_units(content_unit_hrefs, remove_all)
- remove_all = true if remove_all.nil?
- tasks = []
-
- if content_unit_hrefs.sort!.any?
- first_slice = remove_all
- content_unit_hrefs.each_slice(UNIT_LIMIT) do |slice|
- tasks << add_content(slice, first_slice)
- first_slice = false
- end
- # If we're merging composite cv repositories, don't clear out the Pulp repository.
- elsif remove_all
- tasks << remove_all_content
- end
- tasks
- end
-
def copy_content_for_source(source_repository, options = {})
# copy_units_by_href(source_repository.debs.pluck(:pulp_id))
filters = ContentViewDebFilter.where(:id => options[:filter_ids])
@@ -261,7 +244,22 @@ def copy_content_for_source(source_repository, options = {})
content_unit_hrefs = whitelist_ids - blacklist_ids
- copy_units(content_unit_hrefs.uniq, options[:remove_all])
+ pulp_deb_copy_serializer = PulpDebClient::Copy.new
+ pulp_deb_copy_serializer.dependency_solving = false
+ pulp_deb_copy_serializer.config = [{
+ source_repo_version: source_repository.version_href,
+ dest_repo: repository_reference.repository_href,
+ content: content_unit_hrefs,
+ }]
+
+ remove_all = options[:remove_all]
+ remove_all = true if remove_all.nil?
+
+ if remove_all
+ remove_all_content_from_repo(repository_reference.repository_href)
+ end
+
+ copy_content_chunked(pulp_deb_copy_serializer)
end
def regenerate_applicability
diff --git a/app/views/foreman/job_templates/bootc_action.erb b/app/views/foreman/job_templates/bootc_action.erb
new file mode 100644
index 00000000000..18007b0a3ee
--- /dev/null
+++ b/app/views/foreman/job_templates/bootc_action.erb
@@ -0,0 +1,26 @@
+<%#
+kind: job_template
+name: Bootc Action - Script Default
+model: JobTemplate
+job_category: Bootc
+description_format: 'bootc %{action} %{options} %{target}'
+provider_type: script
+feature: katello_bootc_action
+template_inputs:
+- name: action
+ description: 'The bootc action: upgrade, switch, rollback or status'
+ input_type: user
+ options: "upgrade\nswitch\nrollback\nstatus"
+ required: true
+- name: options
+ description: Additional options for bootc action
+ input_type: user
+ required: false
+- name: target
+ description: Target for bootc switch action
+ input_type: user
+ required: false
+%>
+
+bootc <%= input('action') %> <%= input('options') %> <%= input('target') %>
+subscription-manager facts --update
diff --git a/app/views/foreman/job_templates/bootc_rollback.erb b/app/views/foreman/job_templates/bootc_rollback.erb
new file mode 100644
index 00000000000..0ba6ea8332c
--- /dev/null
+++ b/app/views/foreman/job_templates/bootc_rollback.erb
@@ -0,0 +1,13 @@
+<%#
+kind: job_template
+name: Bootc Rollback - Script Default
+job_category: Bootc
+description_format: 'bootc rollback'
+feature: katello_bootc_rollback
+provider_type: script
+foreign_input_sets:
+- template: Bootc Action - Script Default
+ exclude: action, options, target
+%>
+
+<%= render_template('Bootc Action - Script Default', :action => 'rollback') %>
diff --git a/app/views/foreman/job_templates/bootc_status.erb b/app/views/foreman/job_templates/bootc_status.erb
new file mode 100644
index 00000000000..7a1204ad7cd
--- /dev/null
+++ b/app/views/foreman/job_templates/bootc_status.erb
@@ -0,0 +1,13 @@
+<%#
+kind: job_template
+name: Bootc Status - Script Default
+job_category: Bootc
+description_format: 'bootc status %{options}'
+feature: katello_bootc_status
+provider_type: script
+foreign_input_sets:
+- template: Bootc Action - Script Default
+ exclude: action, target
+%>
+
+<%= render_template('Bootc Action - Script Default', :action => 'status') %>
diff --git a/app/views/foreman/job_templates/bootc_switch.erb b/app/views/foreman/job_templates/bootc_switch.erb
new file mode 100644
index 00000000000..b10d1739f66
--- /dev/null
+++ b/app/views/foreman/job_templates/bootc_switch.erb
@@ -0,0 +1,13 @@
+<%#
+kind: job_template
+name: Bootc Switch - Script Default
+job_category: Bootc
+description_format: 'bootc switch %{options} %{target}'
+feature: katello_bootc_switch
+provider_type: script
+foreign_input_sets:
+- template: Bootc Action - Script Default
+ exclude: action
+%>
+
+<%= render_template('Bootc Action - Script Default', :action => 'switch') %>
diff --git a/app/views/foreman/job_templates/bootc_upgrade.erb b/app/views/foreman/job_templates/bootc_upgrade.erb
new file mode 100644
index 00000000000..fc7be23ce8c
--- /dev/null
+++ b/app/views/foreman/job_templates/bootc_upgrade.erb
@@ -0,0 +1,13 @@
+<%#
+kind: job_template
+name: Bootc Upgrade - Script Default
+job_category: Bootc
+description_format: 'bootc upgrade %{options}'
+feature: katello_bootc_upgrade
+provider_type: script
+foreign_input_sets:
+- template: Bootc Action - Script Default
+ exclude: action, target
+%>
+
+<%= render_template('Bootc Action - Script Default', :action => 'upgrade') %>
diff --git a/app/views/foreman/job_templates/flatpak_install.erb b/app/views/foreman/job_templates/flatpak_install.erb
new file mode 100644
index 00000000000..f461ce663d2
--- /dev/null
+++ b/app/views/foreman/job_templates/flatpak_install.erb
@@ -0,0 +1,23 @@
+<%#
+kind: job_template
+name: Install Flatpak application on host
+job_category: Katello
+description_format: 'Install Flatpak application %{Application name} on host'
+provider_type: script
+template_inputs:
+- name: Flatpak remote name
+ description: Name of remote to use on host
+ input_type: user
+ required: false
+- name: Application name
+ description: Name of the application to install
+ input_type: user
+ required: true
+%>
+
+<%
+ remote_name = input('Flatpak remote name')
+ app_name = input('Application name')
+%>
+
+sudo dbus-launch flatpak install <%= remote_name %> <%= app_name %> --assumeyes
\ No newline at end of file
diff --git a/app/views/foreman/job_templates/flatpak_login_action.erb b/app/views/foreman/job_templates/flatpak_login_action.erb
new file mode 100644
index 00000000000..407b52d25db
--- /dev/null
+++ b/app/views/foreman/job_templates/flatpak_login_action.erb
@@ -0,0 +1,30 @@
+<%#
+kind: job_template
+name: Login to flatpak registry via podman
+job_category: Katello
+description_format: 'Login to flatpak registry via podman'
+provider_type: script
+template_inputs:
+- name: Flatpak registry URL
+ description: URL of server/capsule
+ input_type: user
+ required: true
+- name: Username
+ description: Username for container registry login
+ input_type: user
+ required: true
+- name: Password
+ description: Password/Access token for container registry login
+ input_type: user
+ required: true
+ hidden_value: true
+%>
+
+<%
+ server_url = input('Flatpak registry URL')
+ username = input('Username')
+ password = input('Password')
+%>
+
+sudo podman login <%= server_url %> --username <%= username %> --password <%= password %>
+sudo cp /run/containers/0/auth.json /etc/flatpak/oci-auth.json
\ No newline at end of file
diff --git a/app/views/foreman/job_templates/flatpak_setup.erb b/app/views/foreman/job_templates/flatpak_setup.erb
new file mode 100644
index 00000000000..d4d5d9deaa1
--- /dev/null
+++ b/app/views/foreman/job_templates/flatpak_setup.erb
@@ -0,0 +1,27 @@
+<%#
+kind: job_template
+name: Set up Flatpak remote on host
+job_category: Katello
+description_format: 'Set up Flatpak remote on host'
+provider_type: script
+template_inputs:
+- name: Remote Name
+ description: Remote name for Flatpak
+ input_type: user
+ required: true
+- name: Flatpak registry URL
+ description: URL of server/capsule
+ input_type: user
+ required: true
+foreign_input_sets:
+- template: Login to flatpak registry via podman
+ exclude: Flatpak registry URL
+%>
+
+<%
+ remote_name = input('Remote Name')
+ registry_url = input('Flatpak registry URL')
+%>
+
+sudo flatpak remote-add --authenticator-name=org.flatpak.Authenticator.Oci <%= remote_name %> oci+<%= registry_url %>/
+<%= render_template('Login to flatpak registry via podman', 'Flatpak registry URL': registry_url) %>
\ No newline at end of file
diff --git a/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb b/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb
index 855b1c4b270..0cd69d31ef3 100644
--- a/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb
+++ b/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb
@@ -22,7 +22,7 @@ template_inputs:
versions: input('Selected update versions')
) -%>
<% if package_names.empty? -%>
-<%= render_template('Package Action - Ansible Default', :state => 'latest', :name => '"*"') %>
+<%= render_template('Package Action - Ansible Default', :state => 'latest', :name => '*') %>
<% else -%>
---
- hosts: all
diff --git a/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl b/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
index 38c1b04a2b1..d7024c206e7 100644
--- a/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
+++ b/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
@@ -13,6 +13,7 @@ attributes :start_date, :if => lambda { |rule| rule.respond_to?(:start_date) &&
attributes :end_date, :if => lambda { |rule| rule.respond_to?(:end_date) && !rule.end_date.blank? }
attributes :architecture, :if => lambda { |rule| rule.respond_to?(:architecture) && !rule.architecture.blank? }
attributes :types, :if => lambda { |rule| rule.respond_to?(:types) && !rule.types.blank? }
+attributes :allow_other_types, :if => lambda { |rule| rule.respond_to?(:allow_other_types) }
attributes :date_type, :if => lambda { |rule| rule.respond_to?(:date_type) }
attributes :module_stream_id, :if => lambda { |rule| rule.respond_to?(:module_stream_id) && !rule.module_stream_id.blank? }
if @resource&.try(:module_stream)
diff --git a/app/views/katello/api/v2/flatpak_remotes/base.json.rabl b/app/views/katello/api/v2/flatpak_remotes/base.json.rabl
index 95075ae85fa..425b06b772d 100644
--- a/app/views/katello/api/v2/flatpak_remotes/base.json.rabl
+++ b/app/views/katello/api/v2/flatpak_remotes/base.json.rabl
@@ -2,4 +2,4 @@ extends 'katello/api/v2/common/identifier'
extends 'katello/api/v2/common/org_reference'
attributes :name
-attributes :url, :description, :username, :token, :seeded, :registry_url
+attributes :url, :description, :username, :seeded, :registry_url
diff --git a/app/views/katello/api/v2/hosts/base.json.rabl b/app/views/katello/api/v2/hosts/base.json.rabl
index 4bdf894928d..21326cbea0d 100644
--- a/app/views/katello/api/v2/hosts/base.json.rabl
+++ b/app/views/katello/api/v2/hosts/base.json.rabl
@@ -5,14 +5,6 @@ object @resource
attributes :id, :name, :description
-node :operatingsystem_family do |resource|
- resource.operatingsystem&.family
-end
-
-node :operatingsystem_major do |resource|
- resource.operatingsystem&.major
-end
-
if @facet
node :content_view do
content_view = @facet&.single_content_view
diff --git a/app/views/katello/api/v2/hosts/os_attributes.json.rabl b/app/views/katello/api/v2/hosts/os_attributes.json.rabl
new file mode 100644
index 00000000000..963eb4e0207
--- /dev/null
+++ b/app/views/katello/api/v2/hosts/os_attributes.json.rabl
@@ -0,0 +1,13 @@
+object @resource
+
+@resource ||= @object
+
+attributes :id, :name, :description
+
+node :operatingsystem_family do |resource|
+ resource.operatingsystem&.family
+end
+
+node :operatingsystem_major do |resource|
+ resource.operatingsystem&.major
+end
diff --git a/app/views/katello/api/v2/http_proxies/show.json.rabl b/app/views/katello/api/v2/http_proxies/show.json.rabl
new file mode 100644
index 00000000000..9e41a1adf72
--- /dev/null
+++ b/app/views/katello/api/v2/http_proxies/show.json.rabl
@@ -0,0 +1 @@
+node(:content_default_http_proxy) { |sp| sp.content_default_http_proxy? }
diff --git a/app/views/overrides/activation_keys/_host_synced_content_select.html.erb b/app/views/overrides/activation_keys/_host_synced_content_select.html.erb
index a94e6833f67..068050cd177 100644
--- a/app/views/overrides/activation_keys/_host_synced_content_select.html.erb
+++ b/app/views/overrides/activation_keys/_host_synced_content_select.html.erb
@@ -23,9 +23,15 @@
:class => 'form-control', :name => ks_repo_select_name, :disabled => kickstart_options.empty? %>
<% end %>
-<% content_for(:javascripts) do -%>
-
-<% end -%>
\ No newline at end of file
+ }
+ else {
+ $(document).on('ContentLoad', function(){
+ KT.hosts.set_media_selection_bindings();
+ KT.hosts.update_media_enablement();
+ });
+ }
+
diff --git a/app/views/overrides/http_proxies/_update_setting_input.html.erb b/app/views/overrides/http_proxies/_update_setting_input.html.erb
index a9141f4bcde..991456abc43 100644
--- a/app/views/overrides/http_proxies/_update_setting_input.html.erb
+++ b/app/views/overrides/http_proxies/_update_setting_input.html.erb
@@ -1,16 +1,18 @@
-<% if @http_proxy.new_record? %>
-
-
-
-
- <%= check_box_tag 'http_proxy[default_content]',
- '1',
- params.dig('http_proxy', 'default_content') == '1',
- id: 'http_proxy_default_content' %>
- Set this proxy as the default for content, updating the 'Default HTTP Proxy' setting.
-
+
diff --git a/config/initializers/monkeys.rb b/config/initializers/monkeys.rb
index 8ed5b9fe7a5..1cba332c8aa 100644
--- a/config/initializers/monkeys.rb
+++ b/config/initializers/monkeys.rb
@@ -1,4 +1,3 @@
#place where monkey patches are required
require 'monkeys/ar_postgres_evr_t'
-require 'monkeys/fx_sqlite_skip'
require 'monkeys/remove_hidden_distribution'
diff --git a/config/routes/api/registry.rb b/config/routes/api/registry.rb
index e4a365ed80a..d5ff6ec475d 100644
--- a/config/routes/api/registry.rb
+++ b/config/routes/api/registry.rb
@@ -19,6 +19,7 @@ class ActionDispatch::Routing::Mapper
match '/v2' => 'registry_proxies#ping', :via => :get
match '/v1/_ping' => 'registry_proxies#v1_ping', :via => :get
match '/v1/search' => 'registry_proxies#v1_search', :via => :get
+ match '/index/static' => 'registry_proxies#static_index', :via => :get
end
end
end
diff --git a/config/routes/api/v2.rb b/config/routes/api/v2.rb
index c6c894a76fe..d3dd1fa9272 100644
--- a/config/routes/api/v2.rb
+++ b/config/routes/api/v2.rb
@@ -30,6 +30,10 @@ class ActionDispatch::Routing::Mapper
end
end
+ api_resources :host_bootc_images, :only => [:auto_complete_search] do
+ get :auto_complete_search, :on => :collection
+ end
+
api_resources :capsules, :only => [:index, :show] do
member do
resource :content, :only => [], :controller => 'capsule_content' do
diff --git a/config/routes/overrides.rb b/config/routes/overrides.rb
index 142312d6e8d..32bcf9d5fdb 100644
--- a/config/routes/overrides.rb
+++ b/config/routes/overrides.rb
@@ -59,6 +59,7 @@ def matches?(request)
collection do
match '/auto_complete_search' => 'host_autocomplete#auto_complete_search', :via => :get
+ match '/bootc_images' => 'host_bootc_images#bootc_images', :via => :get
match '/bulk/add_host_collections' => 'hosts_bulk_actions#bulk_add_host_collections', :via => :put
match '/bulk/remove_host_collections' => 'hosts_bulk_actions#bulk_remove_host_collections', :via => :put
match '/bulk/remove_host_collections' => 'hosts_bulk_actions#bulk_remove_host_collections', :via => :put
diff --git a/db/migrate/20200213184848_create_evr_type.rb b/db/migrate/20200213184848_create_evr_type.rb
index 8bd6bfe9148..4426fd730ff 100644
--- a/db/migrate/20200213184848_create_evr_type.rb
+++ b/db/migrate/20200213184848_create_evr_type.rb
@@ -4,7 +4,132 @@ class CreateEvrType < ActiveRecord::Migration[5.2]
def up
unless connection.adapter_name.downcase.include?('sqlite')
- enable_extension "evr"
+ execute <<~SQL
+ create type evr_array_item as (
+ n NUMERIC,
+ s TEXT
+ );
+
+ create type evr_t as (
+ epoch INT,
+ version evr_array_item[],
+ release evr_array_item[]
+ );
+
+ CREATE FUNCTION evr_trigger() RETURNS trigger AS $$
+ BEGIN
+ NEW.evr = (select ROW(coalesce(NEW.epoch::numeric,0),
+ rpmver_array(coalesce(NEW.version,'empty'))::evr_array_item[],
+ rpmver_array(coalesce(NEW.release,'empty'))::evr_array_item[])::evr_t);
+ RETURN NEW;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION empty(t TEXT)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ return t ~ '^[[:space:]]*$';
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION isalpha(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('a') and ascii('z') or
+ ascii(ch) between ascii('A') and ascii('Z')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION isalphanum(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('a') and ascii('z') or
+ ascii(ch) between ascii('A') and ascii('Z') or
+ ascii(ch) between ascii('0') and ascii('9')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace function isdigit(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('0') and ascii('9')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END ;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION rpmver_array (string1 IN VARCHAR)
+ RETURNS evr_array_item[] as $$
+ declare
+ str1 VARCHAR := string1;
+ digits VARCHAR(10) := '0123456789';
+ lc_alpha VARCHAR(27) := 'abcdefghijklmnopqrstuvwxyz';
+ uc_alpha VARCHAR(27) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ alpha VARCHAR(54) := lc_alpha || uc_alpha;
+ one VARCHAR;
+ isnum BOOLEAN;
+ ver_array evr_array_item[] := ARRAY[]::evr_array_item[];
+ BEGIN
+ if str1 is NULL
+ then
+ RAISE EXCEPTION 'VALUE_ERROR.';
+ end if;
+
+ one := str1;
+ <
>
+ while one <> ''
+ loop
+ declare
+ segm1 VARCHAR;
+ segm1_n NUMERIC := 0;
+ begin
+ -- Throw out all non-alphanum characters
+ while one <> '' and not isalphanum(one)
+ loop
+ one := substr(one, 2);
+ end loop;
+ str1 := one;
+ if str1 <> '' and isdigit(str1)
+ then
+ str1 := ltrim(str1, digits);
+ isnum := true;
+ else
+ str1 := ltrim(str1, alpha);
+ isnum := false;
+ end if;
+ if str1 <> ''
+ then segm1 := substr(one, 1, length(one) - length(str1));
+ else segm1 := one;
+ end if;
+
+ if segm1 = '' then return ver_array; end if; /* arbitrary */
+ if isnum
+ then
+ segm1 := ltrim(segm1, '0');
+ if segm1 <> '' then segm1_n := segm1::numeric; end if;
+ segm1 := NULL;
+ else
+ end if;
+ ver_array := array_append(ver_array, (segm1_n, segm1)::evr_array_item);
+ one := str1;
+ end;
+ end loop segment_loop;
+
+ return ver_array;
+ END ;
+ $$ language 'plpgsql';
+
+ SQL
add_column :katello_rpms, :evr, :evr_t
add_column :katello_installed_packages, :evr, :evr_t
diff --git a/db/migrate/20240924161240_katello_recreate_evr_constructs.rb b/db/migrate/20240924161240_katello_recreate_evr_constructs.rb
new file mode 100644
index 00000000000..e0a28790d26
--- /dev/null
+++ b/db/migrate/20240924161240_katello_recreate_evr_constructs.rb
@@ -0,0 +1,160 @@
+class KatelloRecreateEvrConstructs < ActiveRecord::Migration[6.1]
+ def up
+ if !extension_enabled?('evr')
+ return
+ else
+ execute <<~SQL
+ DROP EXTENSION evr CASCADE;
+ SQL
+
+ execute <<~SQL
+ create type evr_array_item as (
+ n NUMERIC,
+ s TEXT
+ );
+
+ create type evr_t as (
+ epoch INT,
+ version evr_array_item[],
+ release evr_array_item[]
+ );
+
+ CREATE FUNCTION evr_trigger() RETURNS trigger AS $$
+ BEGIN
+ NEW.evr = (select ROW(coalesce(NEW.epoch::numeric,0),
+ rpmver_array(coalesce(NEW.version,'empty'))::evr_array_item[],
+ rpmver_array(coalesce(NEW.release,'empty'))::evr_array_item[])::evr_t);
+ RETURN NEW;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION empty(t TEXT)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ return t ~ '^[[:space:]]*$';
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION isalpha(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('a') and ascii('z') or
+ ascii(ch) between ascii('A') and ascii('Z')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION isalphanum(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('a') and ascii('z') or
+ ascii(ch) between ascii('A') and ascii('Z') or
+ ascii(ch) between ascii('0') and ascii('9')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END;
+ $$ language 'plpgsql';
+
+ create or replace function isdigit(ch CHAR)
+ RETURNS BOOLEAN as $$
+ BEGIN
+ if ascii(ch) between ascii('0') and ascii('9')
+ then
+ return TRUE;
+ end if;
+ return FALSE;
+ END ;
+ $$ language 'plpgsql';
+
+ create or replace FUNCTION rpmver_array (string1 IN VARCHAR)
+ RETURNS evr_array_item[] as $$
+ declare
+ str1 VARCHAR := string1;
+ digits VARCHAR(10) := '0123456789';
+ lc_alpha VARCHAR(27) := 'abcdefghijklmnopqrstuvwxyz';
+ uc_alpha VARCHAR(27) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ alpha VARCHAR(54) := lc_alpha || uc_alpha;
+ one VARCHAR;
+ isnum BOOLEAN;
+ ver_array evr_array_item[] := ARRAY[]::evr_array_item[];
+ BEGIN
+ if str1 is NULL
+ then
+ RAISE EXCEPTION 'VALUE_ERROR.';
+ end if;
+
+ one := str1;
+ <>
+ while one <> ''
+ loop
+ declare
+ segm1 VARCHAR;
+ segm1_n NUMERIC := 0;
+ begin
+ -- Throw out all non-alphanum characters
+ while one <> '' and not isalphanum(one)
+ loop
+ one := substr(one, 2);
+ end loop;
+ str1 := one;
+ if str1 <> '' and isdigit(str1)
+ then
+ str1 := ltrim(str1, digits);
+ isnum := true;
+ else
+ str1 := ltrim(str1, alpha);
+ isnum := false;
+ end if;
+ if str1 <> ''
+ then segm1 := substr(one, 1, length(one) - length(str1));
+ else segm1 := one;
+ end if;
+
+ if segm1 = '' then return ver_array; end if; /* arbitrary */
+ if isnum
+ then
+ segm1 := ltrim(segm1, '0');
+ if segm1 <> '' then segm1_n := segm1::numeric; end if;
+ segm1 := NULL;
+ else
+ end if;
+ ver_array := array_append(ver_array, (segm1_n, segm1)::evr_array_item);
+ one := str1;
+ end;
+ end loop segment_loop;
+
+ return ver_array;
+ END ;
+ $$ language 'plpgsql';
+
+ SQL
+
+ add_column :katello_rpms, :evr, :evr_t
+ add_column :katello_installed_packages, :evr, :evr_t
+
+ execute <<-SQL
+ update katello_rpms SET evr = (ROW(coalesce(epoch::numeric,0),
+ rpmver_array(coalesce(version,'empty'))::evr_array_item[],
+ rpmver_array(coalesce(release,'empty'))::evr_array_item[])::evr_t);
+
+ update katello_installed_packages SET evr = (ROW(coalesce(epoch::numeric,0),
+ rpmver_array(coalesce(version,'empty'))::evr_array_item[],
+ rpmver_array(coalesce(release,'empty'))::evr_array_item[])::evr_t);
+ SQL
+
+ create_trigger :evr_insert_trigger_katello_rpms, on: :katello_rpms
+ create_trigger :evr_update_trigger_katello_rpms, on: :katello_rpms
+ create_trigger :evr_insert_trigger_katello_installed_packages, on: :katello_installed_packages
+ create_trigger :evr_update_trigger_katello_installed_packages, on: :katello_installed_packages
+ end
+ end
+
+ def down
+ fail ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb b/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb
new file mode 100644
index 00000000000..adac4544e52
--- /dev/null
+++ b/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb
@@ -0,0 +1,6 @@
+class AddAllowOtherTypesToContentViewErratumFilterRules < ActiveRecord::Migration[6.1]
+ def change
+ add_column :katello_content_view_erratum_filter_rules, :allow_other_types, :boolean,
+ :default => false, :null => false
+ end
+end
diff --git a/db/seeds.d/75-job_templates.rb b/db/seeds.d/75-job_templates.rb
index 385bc3f3ae6..17247eb9c39 100644
--- a/db/seeds.d/75-job_templates.rb
+++ b/db/seeds.d/75-job_templates.rb
@@ -1,8 +1,12 @@
User.as_anonymous_admin do
JobTemplate.without_auditing do
+ # let's name root template as *_action.erb
+ root_action_files = Dir[File.join("#{Katello::Engine.root}/app/views/foreman/job_templates/**/*_action.erb")]
template_files = Dir[File.join("#{Katello::Engine.root}/app/views/foreman/job_templates/**/*.erb")]
template_files.reject! { |file| file.end_with?('_ansible_default.erb') } unless Katello.with_ansible?
- template_files.each do |template|
+
+ # root templates need to be imported first
+ (root_action_files + (template_files - root_action_files)).each do |template|
sync = !Rails.env.test? && Setting[:remote_execution_sync_templates]
template = JobTemplate.import_raw!(File.read(template), :default => true, :lock => true, :update => sync)
diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/debs.controller.js b/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/debs.controller.js
index 4cf9adb0c7d..7a2e6ca390e 100644
--- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/debs.controller.js
+++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/debs.controller.js
@@ -1,38 +1,52 @@
-(function () {
- 'use strict';
-
- /**
- * @ngdoc controller
- * @name Bastion.debs.controller:DebsController
- *
- * @description
- * Handles fetching deb packages and populating Nutupane based on the current
- * ui-router state.
- *
- * @requires translate
- *
- */
- function DebsController($scope, $location, translate, Nutupane, Deb, CurrentOrganization) {
+/**
+ * @ngdoc controller
+ * @name Bastion.debs.controller:DebsController
+ *
+ * @description
+ * Handles fetching deb packages and populating Nutupane based on the current
+ * ui-router state.
+ *
+ * @requires translate
+ *
+ */
+angular.module('Bastion.debs').controller('DebsController',
+ ['$scope', '$location', 'translate', 'Nutupane', 'Deb', 'Repository', 'CurrentOrganization',
+ function DebsController($scope, $location, translate, Nutupane, Deb, Repository, CurrentOrganization) {
var nutupane;
var params = {
'organization_id': CurrentOrganization,
'search': $location.search().search || "",
+ 'repository_id': $location.search().repositoryId || null,
'paged': true,
'sort_by': 'name',
'sort_order': 'ASC'
};
- nutupane = new Nutupane(Deb, params);
+ nutupane = $scope.nutupane = new Nutupane(Deb, params);
nutupane.primaryOnly = true;
$scope.table = nutupane.table;
// Labels so breadcrumb strings can be translated
$scope.label = translate('Debs');
+ $scope.repositoriesLabel = translate('Repositories');
$scope.controllerName = 'katello_debs';
+ $scope.repository = {name: translate('All Repositories'), id: 'all'};
+
+ Repository.queryUnpaged({'organization_id': CurrentOrganization, 'content_type': 'deb', 'with_content': 'deb'}, function (response) {
+ $scope.repositories = [$scope.repository];
+ $scope.repositories = $scope.repositories.concat(response.results);
+
+ if ($location.search().repositoryId) {
+ $scope.repository = _.find($scope.repositories, function (repository) {
+ return repository.id === parseInt($location.search().repositoryId, 10);
+ });
+ }
+ });
+
Deb.queryPaged({'organization_id': CurrentOrganization}, function (result) {
$scope.packageCount = result.total;
});
@@ -50,12 +64,22 @@
nutupane.refresh();
};
- }
+ $scope.$watch('repository', function (repository) {
+ var nutupaneParams = nutupane.getParams();
- angular
- .module('Bastion.debs')
- .controller('DebsController', DebsController);
+ if (repository.id === 'all') {
+ nutupaneParams['repository_id'] = null;
+ nutupane.setParams(nutupaneParams);
+ } else {
+ $location.search('repositoryId', repository.id);
+ nutupaneParams['repository_id'] = repository.id;
+ nutupane.setParams(nutupaneParams);
+ }
- DebsController.$inject = ['$scope', '$location', 'translate', 'Nutupane', 'Deb', 'CurrentOrganization'];
+ if (!nutupane.table.initialLoad) {
+ nutupane.refresh();
+ }
+ });
-})();
+ }]
+);
diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/views/debs.html b/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/views/debs.html
index 935a718c314..01c2219bdc4 100644
--- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/views/debs.html
+++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/views/debs.html
@@ -6,6 +6,10 @@
Deb Packages
+
+
+
+