katello 4.8.0.rc2 → 4.8.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/katello/api/v2/simple_content_access_controller.rb +3 -1
- data/app/lib/actions/katello/content_view/publish.rb +2 -9
- data/app/lib/actions/katello/organization/simple_content_access/enable.rb +10 -0
- data/app/lib/actions/katello/organization/simple_content_access/prepare_content_overrides.rb +36 -0
- data/app/lib/actions/katello/organization/simple_content_access/toggle.rb +12 -2
- data/app/lib/katello/util/content_overrides_migrator.rb +98 -0
- data/app/models/katello/content_view.rb +10 -6
- data/app/models/katello/host/content_facet.rb +10 -3
- data/db/migrate/20220110223754_update_disconnected_settings.rb +8 -4
- data/db/migrate/20230119003859_ensure_repo_username_password_nil_not_blank.rb +1 -1
- data/lib/katello/plugin.rb +0 -12
- data/lib/katello/version.rb +1 -1
- data/locale/bn/LC_MESSAGES/katello.mo +0 -0
- data/locale/cs/LC_MESSAGES/katello.mo +0 -0
- data/locale/de/LC_MESSAGES/katello.mo +0 -0
- data/locale/en/LC_MESSAGES/katello.mo +0 -0
- data/locale/es/LC_MESSAGES/katello.mo +0 -0
- data/locale/fr/LC_MESSAGES/katello.mo +0 -0
- data/locale/gu/LC_MESSAGES/katello.mo +0 -0
- data/locale/hi/LC_MESSAGES/katello.mo +0 -0
- data/locale/it/LC_MESSAGES/katello.mo +0 -0
- data/locale/ja/LC_MESSAGES/katello.mo +0 -0
- data/locale/ka/LC_MESSAGES/katello.mo +0 -0
- data/locale/kn/LC_MESSAGES/katello.mo +0 -0
- data/locale/ko/LC_MESSAGES/katello.mo +0 -0
- data/locale/mr/LC_MESSAGES/katello.mo +0 -0
- data/locale/or/LC_MESSAGES/katello.mo +0 -0
- data/locale/pa/LC_MESSAGES/katello.mo +0 -0
- data/locale/pt/LC_MESSAGES/katello.mo +0 -0
- data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
- data/locale/ru/LC_MESSAGES/katello.mo +0 -0
- data/locale/ta/LC_MESSAGES/katello.mo +0 -0
- data/locale/te/LC_MESSAGES/katello.mo +0 -0
- data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
- data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
- data/webpack/components/Content/{ContentPage.js → GenericContentPage.js} +7 -4
- data/webpack/components/Content/__tests__/ContentTable.test.js +1 -1
- data/webpack/components/Content/__tests__/GenericContentPage.test.js +35 -0
- data/webpack/components/Search/SearchText.js +70 -0
- data/webpack/components/Table/EmptyStateMessage.js +2 -2
- data/webpack/components/Table/TableWrapper.js +4 -0
- data/webpack/components/extensions/HostDetails/HostDetailsConstants.js +0 -1
- data/webpack/components/extensions/HostDetails/HostDetailsSelectors.js +0 -6
- data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +2 -2
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/moduleStreamsTab.test.js +76 -0
- data/webpack/components/extensions/SearchBar/SearchBarConstants.js +3 -0
- data/webpack/components/extensions/SearchBar/SearchBarHooks.js +50 -0
- data/webpack/components/extensions/SearchBar/SearchBarReducer.js +14 -0
- data/webpack/components/extensions/SearchBar/SearchBarSelectors.js +5 -0
- data/webpack/redux/reducers/index.js +2 -2
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreate.test.js +1 -13
- data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +1 -0
- data/webpack/scenes/Content/{ContentPage.js → GenericContentPage.js} +2 -2
- data/webpack/scenes/Content/__tests__/contentTable.test.js +2 -2
- data/webpack/scenes/Content/index.js +2 -2
- data/webpack/scenes/ContentViews/Details/Filters/Rules/ContainerTag/AddEditContainerTagRuleModal.js +14 -17
- data/webpack/scenes/ContentViews/Details/Filters/Rules/Package/AddEditPackageRuleModal.js +24 -28
- data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVContainerImageFilterContent.test.js +11 -18
- data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVRpmFilterContent.test.js +10 -23
- data/webpack/scenes/ContentViews/Details/Filters/__tests__/ContentViewPackageGroupFilter.test.js +0 -2
- data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +1 -7
- data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +0 -2
- data/webpack/scenes/ModuleStreams/ModuleStreamsPage.js +2 -2
- data/webpack/scenes/ModuleStreams/__tests__/ModuleStreamPage.test.js +2 -2
- data/webpack/scenes/ModuleStreams/__tests__/__snapshots__/ModuleStreamPage.test.js.snap +1 -1
- data/webpack/scenes/Settings/SettingsConstants.js +2 -3
- data/webpack/scenes/Settings/SettingsReducer.js +2 -16
- data/webpack/scenes/Settings/SettingsSelectors.js +2 -2
- data/webpack/test-utils/react-testing-lib-wrapper.js +0 -6
- metadata +45 -26
- data/webpack/components/Content/__tests__/ContentPage.test.js +0 -32
- data/webpack/components/Content/__tests__/__snapshots__/ContentPage.test.js.snap +0 -89
- data/webpack/components/Search/Search.js +0 -156
- data/webpack/components/Search/__tests__/search.test.js +0 -104
- data/webpack/components/Search/helpers.js +0 -6
- data/webpack/components/Search/index.js +0 -15
- data/webpack/components/TypeAhead/TypeAhead.js +0 -157
- data/webpack/components/TypeAhead/TypeAhead.scss +0 -7
- data/webpack/components/TypeAhead/helpers/commonPropTypes.js +0 -35
- data/webpack/components/TypeAhead/helpers/helpers.js +0 -32
- data/webpack/components/TypeAhead/index.js +0 -3
- data/webpack/components/TypeAhead/pf3Search/TypeAheadInput.js +0 -44
- data/webpack/components/TypeAhead/pf3Search/TypeAheadItems.js +0 -56
- data/webpack/components/TypeAhead/pf3Search/TypeAheadSearch.js +0 -53
- data/webpack/components/TypeAhead/pf4Search/TypeAheadInput.js +0 -66
- data/webpack/components/TypeAhead/pf4Search/TypeAheadInput.scss +0 -12
- data/webpack/components/TypeAhead/pf4Search/TypeAheadItems.js +0 -59
- data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81c709207089041832e6dd1b447a01d0d40dc228480d4b4e43b536c853242662
|
4
|
+
data.tar.gz: 94ec8c4161ff3f63d2a48d4be430b5942b7a40684c4bc39a6c734597ed9f39bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1923b0ae05a6bb6c92b7b807793ce795e4ed90a26e598124a0441b49cb30270ec4829eb0ae0a95944de20bb4db5366b9219ac247667e194b8de88c6a7a933e82
|
7
|
+
data.tar.gz: 062cd9f6c371824bbe22b4b686735eba4ac0ee5ebd316fe4e06fa74b942e58da6f891ef6c3d5ed92158e9d32878bfc533574419b63836d4f4078a3b85eca54bb
|
@@ -26,8 +26,10 @@ module Katello
|
|
26
26
|
api :PUT, "/organizations/:organization_id/simple_content_access/enable",
|
27
27
|
N_("Enable simple content access for a manifest")
|
28
28
|
param :organization_id, :number, :desc => N_("Organization ID"), :required => true
|
29
|
+
param :auto_create_overrides, :bool, :desc => N_("Automatically create disabled content overrides for custom products which do not have an attached subscription"), :required => false, :default => true
|
29
30
|
def enable
|
30
|
-
|
31
|
+
auto_create = params.key?(:auto_create_overrides) ? ::Foreman::Cast.to_bool(params[:auto_create_overrides]) : true
|
32
|
+
task = async_task(::Actions::Katello::Organization::SimpleContentAccess::Enable, params[:organization_id], auto_create_overrides: auto_create)
|
31
33
|
respond_for_async :resource => task
|
32
34
|
end
|
33
35
|
|
@@ -112,15 +112,8 @@ module Actions
|
|
112
112
|
version.update_content_counts!
|
113
113
|
# update errata applicability counts for all hosts in the CV & Library
|
114
114
|
unless input[:skip_promotion]
|
115
|
-
|
116
|
-
|
117
|
-
::Katello::Host::ContentFacet.in_content_views_and_environments(
|
118
|
-
content_views: [content_view],
|
119
|
-
lifecycle_environments: [lifecycle_environment]
|
120
|
-
).each do |facet|
|
121
|
-
facet.update_applicability_counts
|
122
|
-
facet.update_errata_status
|
123
|
-
end
|
115
|
+
environment = ::Katello::KTEnvironment.find(input[:environment_id])
|
116
|
+
::Katello::ContentView.find(input[:content_view_id]).update_host_statuses(environment)
|
124
117
|
end
|
125
118
|
|
126
119
|
history = ::Katello::ContentViewHistory.find(input[:history_id])
|
@@ -3,6 +3,16 @@ module Actions
|
|
3
3
|
module Organization
|
4
4
|
module SimpleContentAccess
|
5
5
|
class Enable < Toggle
|
6
|
+
def plan(organization_id, auto_create_overrides: true)
|
7
|
+
input[:auto_create_overrides] = auto_create_overrides
|
8
|
+
sequence do
|
9
|
+
if auto_create_overrides
|
10
|
+
plan_action(PrepareContentOverrides, organization_id)
|
11
|
+
end
|
12
|
+
super(organization_id) # puts plan_self inside the sequence
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
def content_access_mode_value
|
7
17
|
SIMPLE_CONTENT_ACCESS_ENABLED_VALUE
|
8
18
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Actions
|
2
|
+
module Katello
|
3
|
+
module Organization
|
4
|
+
module SimpleContentAccess
|
5
|
+
class PrepareContentOverrides < Actions::Base
|
6
|
+
def plan(organization_id)
|
7
|
+
Rails.logger.info "PrepareContentOverrides plan: #{organization_id.inspect}"
|
8
|
+
organization = ::Organization.find(organization_id.to_i)
|
9
|
+
org_name = organization.name
|
10
|
+
|
11
|
+
plan_self(organization_id: organization_id, organization_name: org_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
organization = ::Organization.find(input[:organization_id].to_i)
|
16
|
+
migrator = ::Katello::Util::ContentOverridesMigrator.new(organization: organization)
|
17
|
+
|
18
|
+
output[:migrator_result] = migrator.execute_non_sca_overrides!
|
19
|
+
end
|
20
|
+
|
21
|
+
def rescue_strategy
|
22
|
+
Dynflow::Action::Rescue::Skip
|
23
|
+
end
|
24
|
+
|
25
|
+
def humanized_name
|
26
|
+
N_("Prepare content overrides for Simple Content Access")
|
27
|
+
end
|
28
|
+
|
29
|
+
def humanized_input
|
30
|
+
_("for organization %s") % input[:organization_name]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -12,9 +12,15 @@ module Actions
|
|
12
12
|
attr_reader :organization
|
13
13
|
|
14
14
|
def plan(organization_id)
|
15
|
-
|
15
|
+
organization = ::Organization.find(organization_id.to_i)
|
16
|
+
input[:organization_name] = organization.name
|
17
|
+
input[:organization_label] = organization.label
|
16
18
|
action_subject organization
|
17
|
-
|
19
|
+
plan_self(organization_id: organization_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
::Katello::Resources::Candlepin::Owner.update(input[:organization_label], contentAccessMode: content_access_mode_value)
|
18
24
|
end
|
19
25
|
|
20
26
|
def failure_notification(plan)
|
@@ -30,6 +36,10 @@ module Actions
|
|
30
36
|
)
|
31
37
|
end
|
32
38
|
|
39
|
+
def humanized_input
|
40
|
+
_("for organization %s") % input[:organization_name]
|
41
|
+
end
|
42
|
+
|
33
43
|
private
|
34
44
|
|
35
45
|
def consumer
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Katello
|
2
|
+
module Util
|
3
|
+
class ContentOverridesMigrator # used in Actions::Katello::Organization::SimpleContentAccess::PrepareContentOverrides
|
4
|
+
include ActionView::Helpers::TextHelper
|
5
|
+
|
6
|
+
def initialize(organization:)
|
7
|
+
@organization = organization
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute_non_sca_overrides!
|
11
|
+
host_errors = create_disabled_overrides_for_non_sca_org_hosts(organization: @organization)
|
12
|
+
ak_errors = create_disabled_overrides_for_non_sca_org_activation_keys(organization: @organization)
|
13
|
+
|
14
|
+
total_errors = host_errors + ak_errors
|
15
|
+
finish_message = "Finished creating overrides in non-SCA orgs; #{total_errors == 0 ? "no errors" : "#{pluralize(total_errors, "error")}"}"
|
16
|
+
messages = { result: finish_message, errors: total_errors }
|
17
|
+
messages[:host_errors] = "Hosts - #{pluralize(host_errors, "error")} creating disabled overrides for unsubscribed content; see log messages above" if host_errors > 0
|
18
|
+
messages[:ak_errors] = "Activation keys - #{pluralize(ak_errors, "error")} creating disabled overrides for unsubscribed content; see log messages above" if ak_errors > 0
|
19
|
+
messages[:success_message] = "You may now switch all organizations to Simple Content Access mode without any change in access to content." if total_errors == 0
|
20
|
+
Rails.logger.info finish_message
|
21
|
+
Rails.logger.info messages[:host_errors] if messages[:host_errors]
|
22
|
+
Rails.logger.info messages[:ak_errors] if messages[:ak_errors]
|
23
|
+
Rails.logger.info messages[:success_message] if messages[:success_message]
|
24
|
+
messages
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_disabled_overrides_for_non_sca(consumable:)
|
28
|
+
content_finder = ::Katello::ProductContentFinder.new(
|
29
|
+
match_subscription: false,
|
30
|
+
match_environment: false,
|
31
|
+
consumable: consumable
|
32
|
+
)
|
33
|
+
subscribed_content_finder = ::Katello::ProductContentFinder.new(
|
34
|
+
match_subscription: true,
|
35
|
+
match_environment: false,
|
36
|
+
consumable: consumable
|
37
|
+
)
|
38
|
+
candlepin_resource = consumable.is_a?(::Katello::Host::SubscriptionFacet) ? ::Katello::Resources::Candlepin::Consumer : ::Katello::Resources::Candlepin::ActivationKey
|
39
|
+
consumable_id = consumable.is_a?(::Katello::Host::SubscriptionFacet) ? consumable.uuid : consumable.cp_id
|
40
|
+
repos_with_existing_overrides = candlepin_resource.content_overrides(consumable_id).map do |override|
|
41
|
+
override[:contentLabel]
|
42
|
+
end
|
43
|
+
unsubscribed_content = content_finder.custom_content_labels - subscribed_content_finder.custom_content_labels - repos_with_existing_overrides
|
44
|
+
new_overrides = unsubscribed_content.map do |repo_label|
|
45
|
+
::Katello::ContentOverride.new(
|
46
|
+
repo_label,
|
47
|
+
{ name: "enabled", value: "0" } # Override to disabled
|
48
|
+
)
|
49
|
+
end
|
50
|
+
return if new_overrides.blank?
|
51
|
+
if consumable.is_a? ::Katello::Host::SubscriptionFacet
|
52
|
+
::Katello::Resources::Candlepin::Consumer.update_content_overrides(
|
53
|
+
consumable.uuid,
|
54
|
+
new_overrides.map(&:to_entitlement_hash)
|
55
|
+
)
|
56
|
+
else
|
57
|
+
::Katello::Resources::Candlepin::ActivationKey.update_content_overrides(
|
58
|
+
consumable.cp_id,
|
59
|
+
new_overrides.map(&:to_entitlement_hash)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_disabled_overrides_for_non_sca_org_hosts(organization:)
|
65
|
+
errors = 0
|
66
|
+
fail _("Organization must be specified") if organization.blank?
|
67
|
+
return 0 if organization.simple_content_access? # subscription attachment is meaningless with SCA
|
68
|
+
Rails.logger.info("Hosts - Creating disabled overrides for unsubscribed content in organization #{organization.name}")
|
69
|
+
# only registered hosts with content!
|
70
|
+
hosts_to_update = organization.hosts.joins(:subscription_facet).where.not("#{Katello::Host::SubscriptionFacet.table_name}.host_id" => nil)
|
71
|
+
hosts_to_update.each do |host|
|
72
|
+
create_disabled_overrides_for_non_sca(consumable: host.subscription_facet)
|
73
|
+
rescue => e
|
74
|
+
errors += 1
|
75
|
+
Rails.logger.error("Failed to update host #{host.name}: #{e.message}")
|
76
|
+
Rails.logger.debug e.backtrace.join("\n")
|
77
|
+
end
|
78
|
+
errors
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_disabled_overrides_for_non_sca_org_activation_keys(organization:)
|
82
|
+
errors = 0
|
83
|
+
fail _("Organization must be specified") if organization.blank?
|
84
|
+
return 0 if organization.simple_content_access? # subscription attachment is meaningless with SCA
|
85
|
+
Rails.logger.info("Activation keys - Creating disabled overrides for unsubscribed content in organization #{organization.name}")
|
86
|
+
aks_to_update = organization.activation_keys
|
87
|
+
aks_to_update.each do |ak|
|
88
|
+
create_disabled_overrides_for_non_sca(consumable: ak)
|
89
|
+
rescue => e
|
90
|
+
errors += 1
|
91
|
+
Rails.logger.error("Failed to update activation key #{ak.name}: #{e.message}")
|
92
|
+
Rails.logger.debug e.backtrace.join("\n")
|
93
|
+
end
|
94
|
+
errors
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -652,12 +652,16 @@ module Katello
|
|
652
652
|
end
|
653
653
|
|
654
654
|
def check_orphaned_content_facets!(environments: [])
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
655
|
+
Location.no_taxonomy_scope do
|
656
|
+
User.as_anonymous_admin do
|
657
|
+
::Katello::Host::ContentFacet.in_content_views_and_environments(
|
658
|
+
content_views: [self],
|
659
|
+
lifecycle_environments: environments
|
660
|
+
).each do |facet|
|
661
|
+
unless facet.host
|
662
|
+
fail _("Orphaned content facets for deleted hosts exist for the content view and environment. Please run rake task : katello:clean_orphaned_facets and try again!")
|
663
|
+
end
|
664
|
+
end
|
661
665
|
end
|
662
666
|
end
|
663
667
|
end
|
@@ -298,19 +298,26 @@ module Katello
|
|
298
298
|
end
|
299
299
|
|
300
300
|
def katello_agent_installed?
|
301
|
-
self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => 'katello-agent').any?
|
301
|
+
self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => 'katello-agent').any? ||
|
302
|
+
self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => 'katello-agent').any?
|
302
303
|
end
|
303
304
|
|
304
305
|
def tracer_installed?
|
305
306
|
self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => [ "python-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
|
306
307
|
"python3-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
|
307
|
-
HOST_TOOLS_TRACER_PACKAGE_NAME ]).any?
|
308
|
+
HOST_TOOLS_TRACER_PACKAGE_NAME ]).any? ||
|
309
|
+
self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => [ "python-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
|
310
|
+
"python3-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
|
311
|
+
HOST_TOOLS_TRACER_PACKAGE_NAME ]).any?
|
308
312
|
end
|
309
313
|
|
310
314
|
def host_tools_installed?
|
311
315
|
host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => [ "python-#{HOST_TOOLS_PACKAGE_NAME}",
|
312
316
|
"python3-#{HOST_TOOLS_PACKAGE_NAME}",
|
313
|
-
HOST_TOOLS_PACKAGE_NAME ]).any?
|
317
|
+
HOST_TOOLS_PACKAGE_NAME ]).any? ||
|
318
|
+
host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => [ "python-#{HOST_TOOLS_PACKAGE_NAME}",
|
319
|
+
"python3-#{HOST_TOOLS_PACKAGE_NAME}",
|
320
|
+
HOST_TOOLS_PACKAGE_NAME ]).any?
|
314
321
|
end
|
315
322
|
|
316
323
|
def update_errata_status
|
@@ -1,19 +1,23 @@
|
|
1
1
|
class UpdateDisconnectedSettings < ActiveRecord::Migration[6.0]
|
2
|
+
class FakeSetting < Katello::Model
|
3
|
+
self.table_name = 'settings'
|
4
|
+
end
|
5
|
+
|
2
6
|
def up
|
3
|
-
setting_disconnected =
|
7
|
+
setting_disconnected = FakeSetting.find_by(name: 'content_disconnected')
|
4
8
|
setting = Setting.find_by(name: 'subscription_connection_enabled')
|
5
9
|
|
6
10
|
setting&.update!(
|
7
11
|
value: !setting_disconnected&.value
|
8
12
|
)
|
9
|
-
|
13
|
+
FakeSetting.where(:name => 'content_disconnected').delete_all
|
10
14
|
end
|
11
15
|
|
12
16
|
def down
|
13
17
|
remove_column :katello_cdn_configurations, :airgapped
|
14
18
|
setting_disconnected = Setting.find_by(name: 'subscription_connection_enabled')
|
15
|
-
|
16
|
-
|
19
|
+
FakeSetting.set('content_disconnected', N_("A server operating in disconnected mode does not communicate with the Red Hat CDN."),
|
20
|
+
!setting_disconnected.value, N_('Disconnected mode'))
|
17
21
|
|
18
22
|
Setting.where(:name => 'subscription_connection_enabled').delete_all
|
19
23
|
end
|
@@ -2,7 +2,7 @@ class EnsureRepoUsernamePasswordNilNotBlank < ActiveRecord::Migration[6.1]
|
|
2
2
|
def change
|
3
3
|
::Katello::Repository.library.each do |repo|
|
4
4
|
if repo.upstream_username == '' && repo.upstream_password == ''
|
5
|
-
repo.update(upstream_username: nil, upstream_password: nil)
|
5
|
+
repo.root.update(upstream_username: nil, upstream_password: nil)
|
6
6
|
end
|
7
7
|
end
|
8
8
|
end
|
data/lib/katello/plugin.rb
CHANGED
@@ -651,18 +651,6 @@ Foreman::Plugin.register :katello do
|
|
651
651
|
full_name: N_('Applicability Batch Size'),
|
652
652
|
description: N_("Number of host applicability calculations to process per task.")
|
653
653
|
|
654
|
-
setting 'autosearch_while_typing',
|
655
|
-
type: :boolean,
|
656
|
-
default: true,
|
657
|
-
full_name: N_('Autosearch'),
|
658
|
-
description: N_('For pages that support it, automatically perform search while typing in search input.')
|
659
|
-
|
660
|
-
setting 'autosearch_delay',
|
661
|
-
type: :integer,
|
662
|
-
default: 500,
|
663
|
-
full_name: N_('Autosearch delay'),
|
664
|
-
description: N_('If Autosearch is enabled, delay in milliseconds before executing searches while typing.')
|
665
|
-
|
666
654
|
setting 'bulk_load_size',
|
667
655
|
type: :integer,
|
668
656
|
default: 2000,
|
data/lib/katello/version.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -4,8 +4,9 @@ import { Grid, Col, Row, Form, FormGroup } from 'react-bootstrap';
|
|
4
4
|
import SearchBar from 'foremanReact/components/SearchBar';
|
5
5
|
import { getControllerSearchProps } from 'foremanReact/constants';
|
6
6
|
import ContentTable from './ContentTable';
|
7
|
+
import { useClearSearch } from '../extensions/SearchBar/SearchBarHooks';
|
7
8
|
|
8
|
-
const
|
9
|
+
const GenericContentPage = ({
|
9
10
|
header, onSearch, bookmarkController,
|
10
11
|
autocompleteEndpoint, autocompleteQueryParams,
|
11
12
|
updateSearchQuery, initialInputValue,
|
@@ -15,6 +16,7 @@ const ContentPage = ({
|
|
15
16
|
...getControllerSearchProps(autocompleteEndpoint, `searchBar-content-page-${header}`, true, autocompleteQueryParams),
|
16
17
|
controller: bookmarkController,
|
17
18
|
};
|
19
|
+
const searchBarKey = useClearSearch({ updateSearchQuery });
|
18
20
|
return (
|
19
21
|
<Grid bsClass="container-fluid">
|
20
22
|
<Row>
|
@@ -27,6 +29,7 @@ const ContentPage = ({
|
|
27
29
|
<Form className="toolbar-pf-actions">
|
28
30
|
<FormGroup className="toolbar-pf toolbar-pf-filter">
|
29
31
|
<SearchBar
|
32
|
+
key={searchBarKey}
|
30
33
|
data={searchDataProp}
|
31
34
|
onSearch={onSearch}
|
32
35
|
onSearchChange={updateSearchQuery}
|
@@ -49,7 +52,7 @@ const ContentPage = ({
|
|
49
52
|
);
|
50
53
|
};
|
51
54
|
|
52
|
-
|
55
|
+
GenericContentPage.propTypes = {
|
53
56
|
header: PropTypes.string.isRequired,
|
54
57
|
content: PropTypes.shape({}).isRequired,
|
55
58
|
tableSchema: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
@@ -62,10 +65,10 @@ ContentPage.propTypes = {
|
|
62
65
|
bookmarkController: PropTypes.string,
|
63
66
|
};
|
64
67
|
|
65
|
-
|
68
|
+
GenericContentPage.defaultProps = {
|
66
69
|
autocompleteEndpoint: undefined,
|
67
70
|
autocompleteQueryParams: undefined,
|
68
71
|
bookmarkController: undefined,
|
69
72
|
};
|
70
73
|
|
71
|
-
export default
|
74
|
+
export default GenericContentPage;
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { shallow } from 'enzyme';
|
3
3
|
import toJson from 'enzyme-to-json';
|
4
|
-
import ContentTable from '../ContentTable';
|
5
4
|
import { LoadingState } from '../../../components/LoadingState';
|
6
5
|
import { Table } from '../../../components/pf3Table';
|
6
|
+
import ContentTable from '../ContentTable';
|
7
7
|
|
8
8
|
describe('Content Table', () => {
|
9
9
|
it('should render and contain appropriate components', async () => {
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { act } from 'react-test-renderer';
|
3
|
+
import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
|
4
|
+
import { CONTENT_KEY } from '../../../scenes/Content/ContentConstants';
|
5
|
+
import GenericContentPage from '../GenericContentPage';
|
6
|
+
|
7
|
+
const renderOptions = () => ({
|
8
|
+
apiNamespace: CONTENT_KEY,
|
9
|
+
});
|
10
|
+
|
11
|
+
test('Can render the basic component with no data', async (done) => {
|
12
|
+
const contentHeader = 'Content Header';
|
13
|
+
const content = { results: [] };
|
14
|
+
const onSearch = jest.fn();
|
15
|
+
const updateSearchQuery = jest.fn();
|
16
|
+
const searchQuery = '';
|
17
|
+
const onPaginationChange = jest.fn();
|
18
|
+
const TableSchema = [];
|
19
|
+
const bookmarkController = 'module_streams';
|
20
|
+
|
21
|
+
const { getByText } = renderWithRedux(<GenericContentPage
|
22
|
+
header={contentHeader}
|
23
|
+
content={content}
|
24
|
+
tableSchema={TableSchema}
|
25
|
+
onSearch={onSearch}
|
26
|
+
updateSearchQuery={updateSearchQuery}
|
27
|
+
initialInputValue={searchQuery}
|
28
|
+
onPaginationChange={onPaginationChange}
|
29
|
+
bookmarkController={bookmarkController}
|
30
|
+
/>, renderOptions());
|
31
|
+
|
32
|
+
await patientlyWaitFor(() =>
|
33
|
+
expect(getByText(contentHeader)).toBeInTheDocument());
|
34
|
+
act(done);
|
35
|
+
});
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { SearchAutocomplete } from 'foremanReact/components/SearchBar/SearchAutocomplete';
|
4
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
5
|
+
import { STATUS } from 'foremanReact/constants';
|
6
|
+
import { noop } from 'foremanReact/common/helpers';
|
7
|
+
|
8
|
+
const SearchText = ({
|
9
|
+
data: {
|
10
|
+
autocomplete: { url, apiParams } = { url: '' },
|
11
|
+
disabled,
|
12
|
+
},
|
13
|
+
initialQuery,
|
14
|
+
onSearchChange,
|
15
|
+
name,
|
16
|
+
}) => {
|
17
|
+
const [search, setSearch] = useState(initialQuery || '');
|
18
|
+
const getAPIparams = input => ({ ...apiParams(input) });
|
19
|
+
const { response, status, setAPIOptions } = useAPI('get', url, {
|
20
|
+
params: getAPIparams(search),
|
21
|
+
});
|
22
|
+
const onChange = (newValue) => {
|
23
|
+
onSearchChange(newValue);
|
24
|
+
setSearch(newValue);
|
25
|
+
setAPIOptions({ params: { ...getAPIparams(newValue) } });
|
26
|
+
};
|
27
|
+
const error =
|
28
|
+
status === STATUS.ERROR || response?.[0]?.error
|
29
|
+
? response?.[0]?.error || response.message
|
30
|
+
: null;
|
31
|
+
|
32
|
+
let results = [];
|
33
|
+
if (Array.isArray(response) && !error) {
|
34
|
+
results = response.map(item => ({ label: item, category: '' }));
|
35
|
+
}
|
36
|
+
|
37
|
+
return (
|
38
|
+
<div className="foreman-search-text">
|
39
|
+
<SearchAutocomplete
|
40
|
+
results={results}
|
41
|
+
onSearchChange={onChange}
|
42
|
+
value={search}
|
43
|
+
disabled={disabled}
|
44
|
+
error={error}
|
45
|
+
name={name}
|
46
|
+
/>
|
47
|
+
</div>
|
48
|
+
);
|
49
|
+
};
|
50
|
+
|
51
|
+
SearchText.propTypes = {
|
52
|
+
data: PropTypes.shape({
|
53
|
+
autocomplete: PropTypes.shape({
|
54
|
+
url: PropTypes.string.isRequired,
|
55
|
+
apiParams: PropTypes.func,
|
56
|
+
}).isRequired,
|
57
|
+
disabled: PropTypes.bool,
|
58
|
+
}).isRequired,
|
59
|
+
initialQuery: PropTypes.string,
|
60
|
+
onSearchChange: PropTypes.func,
|
61
|
+
name: PropTypes.string,
|
62
|
+
};
|
63
|
+
|
64
|
+
SearchText.defaultProps = {
|
65
|
+
initialQuery: '',
|
66
|
+
onSearchChange: noop,
|
67
|
+
name: null,
|
68
|
+
};
|
69
|
+
|
70
|
+
export default SearchText;
|
@@ -14,7 +14,7 @@ import { translate as __ } from 'foremanReact/common/I18n';
|
|
14
14
|
import { CubeIcon, ExclamationCircleIcon, SearchIcon, CheckCircleIcon, PlusCircleIcon } from '@patternfly/react-icons';
|
15
15
|
import { global_danger_color_200 as dangerColor, global_success_color_100 as successColor } from '@patternfly/react-tokens';
|
16
16
|
import { useDispatch, useSelector } from 'react-redux';
|
17
|
-
import {
|
17
|
+
import { selectSearchBarClearSearch } from '../extensions/SearchBar/SearchBarSelectors';
|
18
18
|
|
19
19
|
const KatelloEmptyStateIcon = ({
|
20
20
|
error, search, customIcon, happyIcon,
|
@@ -51,7 +51,7 @@ const EmptyStateMessage = ({
|
|
51
51
|
const defaultSecondaryActionText = searchIsActive ? __('Clear search') : __('Clear filters');
|
52
52
|
const secondaryActionText = secondaryActionTextOverride || defaultSecondaryActionText;
|
53
53
|
const dispatch = useDispatch();
|
54
|
-
const clearSearch = useSelector(
|
54
|
+
const clearSearch = useSelector(selectSearchBarClearSearch);
|
55
55
|
const showSecondaryActionAnchor = showSecondaryAction && secondaryActionLink;
|
56
56
|
const handleClick = () => {
|
57
57
|
if (searchIsActive) {
|
@@ -14,6 +14,7 @@ import MainTable from './MainTable';
|
|
14
14
|
import { getPageStats } from './helpers';
|
15
15
|
import SelectAllCheckbox from '../SelectAllCheckbox';
|
16
16
|
import { orgId } from '../../services/api';
|
17
|
+
import { useClearSearch } from '../extensions/SearchBar/SearchBarHooks';
|
17
18
|
|
18
19
|
/* Patternfly 4 table wrapper */
|
19
20
|
const TableWrapper = ({
|
@@ -132,6 +133,8 @@ const TableWrapper = ({
|
|
132
133
|
spawnFetch();
|
133
134
|
}, [searchQuery, spawnFetch, additionalListeners]);
|
134
135
|
|
136
|
+
const searchBarKey = useClearSearch({ updateSearchQuery });
|
137
|
+
|
135
138
|
// If the new page wouldn't exist because of a perPage change,
|
136
139
|
// we should set the current page to the last page.
|
137
140
|
const validatePagination = (data) => {
|
@@ -194,6 +197,7 @@ const TableWrapper = ({
|
|
194
197
|
data={searchDataProp}
|
195
198
|
initialQuery={searchQuery}
|
196
199
|
onSearch={search => updateSearchQuery(search)}
|
200
|
+
key={searchBarKey}
|
197
201
|
/>
|
198
202
|
</FlexItem>
|
199
203
|
}
|
@@ -14,9 +14,3 @@ export const selectHostDetailsStatus = state =>
|
|
14
14
|
|
15
15
|
export const selectHostDetailsError = state =>
|
16
16
|
selectAPIError(state, HOST_DETAILS_KEY);
|
17
|
-
|
18
|
-
export const selectHostDetailsState = state =>
|
19
|
-
state.katello.hostDetails;
|
20
|
-
|
21
|
-
export const selectHostDetailsClearSearch = state =>
|
22
|
-
selectHostDetailsState(state).clearSearch;
|
@@ -49,8 +49,8 @@ import {
|
|
49
49
|
const moduleStreamSupported = ({ os, version }) =>
|
50
50
|
os.match(/RedHat|RHEL|CentOS|Rocky|AlmaLinux|OracleLinux/i) && Number(version) > 7;
|
51
51
|
export const hideModuleStreamsTab = ({ hostDetails }) => {
|
52
|
-
const osMatch = hostDetails?.operatingsystem_name?.match(/(\
|
53
|
-
if (!osMatch) return
|
52
|
+
const osMatch = hostDetails?.operatingsystem_name?.match(/(\D+) (\d+)/);
|
53
|
+
if (!osMatch) return false;
|
54
54
|
const [, os, version] = osMatch;
|
55
55
|
return !(osMatch && moduleStreamSupported({ os, version }));
|
56
56
|
};
|