katello 4.20.0 → 4.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/v2/content_uploads_controller.rb +2 -1
  3. data/app/controllers/katello/api/v2/host_bootc_images_controller.rb +12 -4
  4. data/app/lib/katello/resources/cdn.rb +10 -5
  5. data/app/models/katello/cdn_configuration.rb +4 -2
  6. data/app/models/katello/host/content_facet.rb +15 -1
  7. data/app/services/katello/managed_content_medium_provider.rb +1 -1
  8. data/app/services/katello/pulp3/repository.rb +13 -4
  9. data/app/views/katello/api/v2/content_credentials/show.json.rabl +3 -3
  10. data/app/views/katello/api/v2/content_facet/base.json.rabl +4 -0
  11. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -0
  12. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-errata-modal.controller.js +9 -2
  13. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +1 -1
  14. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/details/deb.controller.js +9 -2
  15. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/details/views/deb-info.html +3 -3
  16. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/packages/details/package.controller.js +6 -3
  17. data/lib/katello/version.rb +1 -1
  18. data/locale/action_names.rb +0 -2
  19. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  20. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  21. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  22. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  23. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  24. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  25. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  26. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  27. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  28. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  29. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  30. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  31. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  32. data/webpack/components/Bookmark/AddBookmarkModal.js +1 -1
  33. data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +7 -7
  34. data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +9 -107
  35. data/webpack/scenes/AlternateContentSources/Create/__tests__/contentCredentials.fixtures.json +6 -6
  36. data/webpack/scenes/ContainerImages/Booted/BootedContainerImagesPage.js +1 -1
  37. data/webpack/scenes/ContentCredentials/__tests__/contentCredentials.fixtures.js +6 -6
  38. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6906fd0237d8f131b8dd191803695f7af15dc79908dd2d1e6d3385394bedc997
4
- data.tar.gz: 20cd7a88f0eae1a31a6ba650e2d761626ebbdd70f3ebc9ae21d362c8144bdd1e
3
+ metadata.gz: 752107902c268ac7230e4ba7c967a1f273c871559b80dd30ff622337d2cb2f56
4
+ data.tar.gz: 514cbd47032ce0940e1246d2cd5b23f290958289858241bfc1e2bff9caa3928f
5
5
  SHA512:
6
- metadata.gz: c973d2b28b03b6dbfe9a3373fd57edd647974e25dc56531630809c3b14c4f3528becbf28725dd7d1b00200149870a81e1a0d45db855b69d484b4c9d967e5afeb
7
- data.tar.gz: ce0c166fef878d6a477e6f003dcda75364d95026237f695db2689bbdbbf06731d3df3ac5a0610e6ce04388cd845df2dfb44cd71c463536748be0f02896f95889
6
+ metadata.gz: ec64a4c18b636df115542a86fa8874c9db757bde3fac4084f23b8ff0aac7baa51fcaf79d88cd684d5c83862e229f743aca63d5d033b9bec9fef7a762743948af
7
+ data.tar.gz: 6430af999d6bfb5478fab680b5ece3986c12841da4fedb5d7cbce05fd381d6ed96b58b71d89a3df59d436a4d88629faeef4afc18ddd39c4869280028d1890a00
@@ -15,7 +15,8 @@ module Katello
15
15
  param :content_type, RepositoryTypeManager.uploadable_content_types(false).map(&:label), :required => false, :desc => N_("content type ('deb', 'file', 'ostree_ref', 'rpm', 'srpm')")
16
16
  def create
17
17
  fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Ansible collections.") if @repository.ansible_collection?
18
- fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload container content via Hammer/API. Use podman push instead.") if @repository.docker?
18
+ # params[:size].to_i > 0 confirms if user is trying to upload a docker image.
19
+ fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload container content via Hammer/API. Use podman push instead.") if @repository.docker? && params[:size].to_i > 0
19
20
  content_type = params[:content_type] || ::Katello::RepositoryTypeManager.find(@repository.content_type)&.default_managed_content_type&.label
20
21
  RepositoryTypeManager.check_content_matches_repo_type!(@repository, content_type)
21
22
  if ::Katello::RepositoryTypeManager.generic_content_type?(content_type)
@@ -10,13 +10,15 @@ module Katello
10
10
  api :GET, "/hosts/bootc_images", N_("List booted bootc container images for hosts")
11
11
  param_group :search, Api::V2::ApiController
12
12
  def bootc_images
13
- params[:sort_by] ||= 'bootc_booted_image'
14
- params[:sort_order] ||= 'asc'
15
13
  if params[:order]
16
- params[:order] = "#{params[:order].split(' ')[0]} #{sanitize_sort_order(params[:order].split(' ')[1])}"
14
+ parts = params[:order].split(' ')
15
+ params[:sort_by] = sanitize_sort_column(parts[0])
16
+ params[:sort_order] = sanitize_sort_order(parts[1])
17
17
  else
18
- params[:order] = "#{params[:sort_by]} #{sanitize_sort_order(params[:sort_order])}"
18
+ params[:sort_by] = sanitize_sort_column(params[:sort_by])
19
+ params[:sort_order] = sanitize_sort_order(params[:sort_order])
19
20
  end
21
+ params[:order] = "#{params[:sort_by]} #{params[:sort_order]}"
20
22
  per_page = params[:per_page].present? ? params[:per_page].to_i : Setting[:entries_per_page]
21
23
  page = params[:page].present? ? params[:page].to_i : 1
22
24
 
@@ -38,6 +40,8 @@ module Katello
38
40
 
39
41
  private
40
42
 
43
+ SORTABLE_COLUMNS = %w[bootc_booted_image bootc_booted_digest host_count].freeze
44
+
41
45
  def sanitize_sort_order(sort_order)
42
46
  if sort_order.present? && ['asc', 'desc'].include?(sort_order.downcase)
43
47
  sort_order.downcase
@@ -46,6 +50,10 @@ module Katello
46
50
  end
47
51
  end
48
52
 
53
+ def sanitize_sort_column(sort_column)
54
+ SORTABLE_COLUMNS.include?(sort_column) ? sort_column : 'bootc_booted_image'
55
+ end
56
+
49
57
  def index_relation
50
58
  query = resource_class.authorized(:view_hosts).distinct
51
59
  query.joins(:content_facet).where.not(bootc_booted_image: nil, bootc_booted_digest: nil)
@@ -34,15 +34,16 @@ module Katello
34
34
  :ssl_ca_cert,
35
35
  :custom_cdn)
36
36
 
37
+ @cert_store = OpenSSL::X509::Store.new
37
38
  if options[:ssl_ca_cert].present?
38
- @cert_store = OpenSSL::X509::Store.new
39
39
  Foreman::Util.add_ca_bundle_to_store(options[:ssl_ca_cert], @cert_store)
40
40
  elsif options[:ssl_ca_file]
41
- @cert_store = OpenSSL::X509::Store.new
42
41
  @cert_store.add_file(options[:ssl_ca_file])
42
+ else
43
+ @cert_store.set_default_paths
43
44
  end
44
45
 
45
- if @cert_store && proxy&.cacert&.present?
46
+ if proxy&.cacert&.present?
46
47
  Foreman::Util.add_ca_bundle_to_store(proxy.cacert, @cert_store)
47
48
  end
48
49
 
@@ -59,8 +60,12 @@ module Katello
59
60
  options[:ssl_ca_file] = self.ca_file
60
61
  self.new(cdn_configuration.url, options)
61
62
  elsif cdn_configuration.custom_cdn?
62
- options[:ssl_ca_cert] = cdn_configuration.ssl_ca
63
- if cdn_configuration.custom_cdn_auth_enabled?
63
+ if cdn_configuration.ssl_ca.present?
64
+ options[:ssl_ca_cert] = cdn_configuration.ssl_ca
65
+ elsif cdn_configuration.redhat_cdn_host?
66
+ options[:ssl_ca_file] = self.ca_file
67
+ end
68
+ if cdn_configuration.custom_cdn_auth_enabled? || cdn_configuration.redhat_cdn_host?
64
69
  options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(product.certificate)
65
70
  options[:ssl_client_key] = OpenSSL::PKey::RSA.new(product.key)
66
71
  end
@@ -41,8 +41,10 @@ module Katello
41
41
  custom_cdn_auth_enabled
42
42
  end
43
43
 
44
- def redhat_cdn_url?
45
- Katello::Resources::CDN::CdnResource.redhat_cdn?(url)
44
+ def redhat_cdn_host?
45
+ URI.parse(url).host&.end_with?('.redhat.com')
46
+ rescue URI::InvalidURIError
47
+ false
46
48
  end
47
49
 
48
50
  def export_sync?
@@ -194,6 +194,19 @@ module Katello
194
194
  content_view_environments.first.default_environment?
195
195
  end
196
196
 
197
+ def content_view_environments_all_default_or_rolling?
198
+ return if content_view_environments.blank?
199
+ # Returns true if all applicable errata are installable, meaning:
200
+ # - First CVEnv is Library (Default Org View + Library environment), OR
201
+ # - All CVEnvs are rolling CVs or Library hosts
202
+ # This determines whether the Applicable/Installable toggle should be hidden.
203
+ return true if content_view_environments.first.default_environment?
204
+
205
+ content_view_environments.all? do |cve|
206
+ cve.content_view&.rolling? || cve.default_environment?
207
+ end
208
+ end
209
+
197
210
  def update_repositories_by_paths(paths)
198
211
  prefixes = %w(/pulp/deb/ /pulp/repos/ /pulp/content/)
199
212
  relative_paths = []
@@ -337,9 +350,10 @@ module Katello
337
350
  facet = host.content_facet || host.build_content_facet
338
351
  attrs_to_add = {}
339
352
  BOOTC_FIELD_FACT_NAMES.each do |fact_name|
353
+ next unless parser.facts.key?(fact_name)
340
354
  fact_value = parser.facts[fact_name]
341
355
  field_name = fact_name.tr(".", "_")
342
- attrs_to_add[field_name] = fact_value # overwrite with nil if fact is not present
356
+ attrs_to_add[field_name] = fact_value
343
357
  end
344
358
  if attrs_to_add['bootc_booted_digest'].present?
345
359
  manifest_entity = find_manifest_entity(digest: attrs_to_add['bootc_booted_digest'])
@@ -17,7 +17,7 @@ module Katello
17
17
  # If there are any 'AppStream' variants, we need to make them
18
18
  # available to Anaconda
19
19
  def additional_media
20
- appstream_repos = entity.operatingsystem.variant_repos(entity, 'AppStream')
20
+ appstream_repos = entity.operatingsystem.try(:variant_repos, entity, 'AppStream') || []
21
21
  super + (appstream_repos.present? ? appstream_repos : [])
22
22
  end
23
23
 
@@ -509,8 +509,12 @@ module Katello
509
509
  }
510
510
  elsif root.redhat? && root.cdn_configuration.custom_cdn?
511
511
  options = {
512
- ca_cert: root.cdn_configuration.ssl_ca,
512
+ ca_cert: custom_cdn_ca_cert,
513
513
  }
514
+ if root.cdn_configuration.redhat_cdn_host?
515
+ options[:client_cert] = root.product.certificate
516
+ options[:client_key] = root.product.key
517
+ end
514
518
  elsif root.redhat? && root.cdn_configuration.network_sync?
515
519
  options = {
516
520
  client_cert: root.cdn_configuration.ssl_cert,
@@ -524,13 +528,18 @@ module Katello
524
528
  ca_cert: root.ssl_ca_cert&.content,
525
529
  }
526
530
  end
527
- append_proxy_cacert(options) if options.key?(:cacert)
531
+ append_proxy_cacert(options) if options.key?(:ca_cert)
528
532
  options
529
533
  end
530
534
 
535
+ def custom_cdn_ca_cert
536
+ return root.cdn_configuration.ssl_ca if root.cdn_configuration.ssl_ca.present?
537
+ ::File.read(::Katello::Resources::CDN::CdnResource.ca_file) if root.cdn_configuration.redhat_cdn_host?
538
+ end
539
+
531
540
  def append_proxy_cacert(options)
532
- if root.http_proxy&.cacert&.present? && options.key?(:cacert)
533
- options[:cacert] += "\n#{root.http_proxy&.cacert}"
541
+ if root.http_proxy&.cacert&.present? && options.key?(:ca_cert)
542
+ options[:ca_cert] = [options[:ca_cert], root.http_proxy.cacert].compact.join("\n")
534
543
  end
535
544
  options
536
545
  end
@@ -114,8 +114,8 @@ end
114
114
 
115
115
  node :permissions do |content_credential|
116
116
  {
117
- :view_content_credenials => content_credential.readable?,
118
- :edit_content_credenials => content_credential.editable?,
119
- :destroy_content_credenials => content_credential.deletable?,
117
+ :view_content_credentials => content_credential.readable?,
118
+ :edit_content_credentials => content_credential.editable?,
119
+ :destroy_content_credentials => content_credential.deletable?,
120
120
  }
121
121
  end
@@ -44,6 +44,10 @@ node :multi_content_view_environment do |content_facet|
44
44
  content_facet.multi_content_view_environment?
45
45
  end
46
46
 
47
+ node :content_view_environments_all_default_or_rolling do |content_facet|
48
+ content_facet.content_view_environments_all_default_or_rolling?
49
+ end
50
+
47
51
  node :allow_multiple_content_views do
48
52
  Setting['allow_multiple_content_views']
49
53
  end
@@ -34,6 +34,7 @@
34
34
  });
35
35
  angular.module('Bastion.auth').value('Permissions', angular.fromJson(`<%= User.current.cached_roles.collect { |role| role.permissions }.flatten.to_json.html_safe %>`));
36
36
  angular.module('Bastion').value('newHostDetailsUI', "<%= Setting[:host_details_ui] %>");
37
+ angular.module('Bastion').value('newHostsPage', "<%= Setting[:new_hosts_page] %>");
37
38
  angular.module('Bastion').value('experimentalLabsSetting', "<%= Setting[:lab_features] %>");
38
39
  </script>
39
40
 
@@ -17,13 +17,14 @@
17
17
  * @requires BastionConfig
18
18
  * @requires hostIds
19
19
  * @requires newHostDetailsUI
20
+ * @requires newHostsPage
20
21
  *
21
22
  * @description
22
23
  * A controller for providing bulk action functionality to the content hosts page.
23
24
  */
24
25
  angular.module('Bastion.content-hosts').controller('ContentHostsBulkErrataModalController',
25
- ['$scope', '$http', '$location', '$window', '$timeout', '$uibModalInstance', 'HostBulkAction', 'HostCollection', 'Nutupane', 'CurrentOrganization', 'Erratum', 'Notification', 'BastionConfig', 'hostIds', 'newHostDetailsUI',
26
- function ($scope, $http, $location, $window, $timeout, $uibModalInstance, HostBulkAction, HostCollection, Nutupane, CurrentOrganization, Erratum, Notification, BastionConfig, hostIds, newHostDetailsUI) {
26
+ ['$scope', '$http', '$location', '$window', '$timeout', '$uibModalInstance', 'HostBulkAction', 'HostCollection', 'Nutupane', 'CurrentOrganization', 'Erratum', 'Notification', 'BastionConfig', 'hostIds', 'newHostDetailsUI', 'newHostsPage',
27
+ function ($scope, $http, $location, $window, $timeout, $uibModalInstance, HostBulkAction, HostCollection, Nutupane, CurrentOrganization, Erratum, Notification, BastionConfig, hostIds, newHostDetailsUI, newHostsPage) {
27
28
  function fetchErratum(errataId) {
28
29
  $scope.erratum = Erratum.get({id: errataId, 'organization_id': CurrentOrganization});
29
30
  }
@@ -43,6 +44,12 @@ angular.module('Bastion.content-hosts').controller('ContentHostsBulkErrataModalC
43
44
  $scope.allHostsSelected = hostIds.allResultsSelected;
44
45
  $scope.hostToolingEnabled = BastionConfig.hostToolingEnabled;
45
46
  $scope.newHostDetailsUI = newHostDetailsUI;
47
+ $scope.newHostsPage = (newHostsPage === 'true');
48
+
49
+ $scope.hostsUrl = function(errataId) {
50
+ var basePath = $scope.newHostsPage ? '/new/hosts' : '/hosts';
51
+ return basePath + '?search=installable_errata%3D' + errataId;
52
+ };
46
53
 
47
54
  $scope.errataActionFormValues = {
48
55
  authenticityToken: $window.AUTH_TOKEN.replace(/&quot;/g, '')
@@ -100,7 +100,7 @@
100
100
  <td class="small" bst-table-cell>{{ erratum.issued }}</td>
101
101
  <td class="small" bst-table-cell class="number-cell">
102
102
  <span ng-switch="newHostDetailsUI">
103
- <a ng-switch-when="true" target="_blank" href="{{ '/hosts?search=installable_errata%3D' + erratum.errata_id }}">
103
+ <a ng-switch-when="true" target="_blank" href="{{ hostsUrl(erratum.errata_id) }}">
104
104
  {{ erratum.affected_hosts_count }}
105
105
  <span class="fa fa-external-link"></span>
106
106
  </a>
@@ -7,11 +7,12 @@
7
7
  * @requires Host
8
8
  * @requires CurrentOrganization
9
9
  * @requires newHostDetailsUI
10
+ * @requires newHostsPage
10
11
  *
11
12
  * @description
12
13
  * Provides the functionality for the debs details action pane.
13
14
  */
14
- function DebController($scope, Deb, Host, CurrentOrganization, ApiErrorHandler, newHostDetailsUI) {
15
+ function DebController($scope, Deb, Host, CurrentOrganization, ApiErrorHandler, newHostDetailsUI, newHostsPage) {
15
16
  $scope.panel = {
16
17
  error: false,
17
18
  loading: true
@@ -23,6 +24,12 @@
23
24
 
24
25
  $scope.installedPackageCount = undefined;
25
26
  $scope.newHostDetailsUI = (newHostDetailsUI === 'true');
27
+ $scope.newHostsPage = (newHostsPage === 'true');
28
+
29
+ $scope.newHostUrl = function(field) {
30
+ var basePath = $scope.newHostsPage ? '/new/hosts' : '/hosts';
31
+ return basePath + '?search=' + $scope.createSearchString(field);
32
+ };
26
33
 
27
34
  $scope.fetchHostCount = function() {
28
35
  Host.get({'per_page': 0, 'search': $scope.createRawSearchString('installed_deb'), 'organization_id': CurrentOrganization}, function (data) {
@@ -51,6 +58,6 @@
51
58
  .module('Bastion.debs')
52
59
  .controller('DebController', DebController);
53
60
 
54
- DebController.$inject = ['$scope', 'Deb', 'Host', 'CurrentOrganization', 'ApiErrorHandler', 'newHostDetailsUI'];
61
+ DebController.$inject = ['$scope', 'Deb', 'Host', 'CurrentOrganization', 'ApiErrorHandler', 'newHostDetailsUI', 'newHostsPage'];
55
62
 
56
63
  })();
@@ -9,7 +9,7 @@
9
9
  <dd>
10
10
  <i class="fa fa-spinner fa-spin" ng-show="installedDebCount === undefined"></i>
11
11
  <span ng-show="installedDebCount !== undefined">
12
- <a ng-if="newHostDetailsUI" href="{{ '/hosts?search=' + createSearchString('installed_deb') }}" target="_blank" rel="noreferrer noopener" translate>
12
+ <a ng-if="newHostDetailsUI" href="{{ newHostUrl('installed_deb') }}" target="_blank" rel="noreferrer noopener" translate>
13
13
  {{ installedDebCount }} Host(s)
14
14
  </a>
15
15
  <a ng-if="!newHostDetailsUI" href="{{ '/content_hosts?search=' + createSearchString('installed_deb') }}" target="_blank" rel="noreferrer noopener" translate>
@@ -20,7 +20,7 @@
20
20
 
21
21
  <dt translate>Applicable To</dt>
22
22
  <dd>
23
- <a ng-if="newHostDetailsUI" href="{{ '/hosts?search=' + createSearchString('applicable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
23
+ <a ng-if="newHostDetailsUI" href="{{ newHostUrl('applicable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
24
24
  {{ deb.hosts_applicable_count }} Host(s)
25
25
  </a>
26
26
  <a ng-if="!newHostDetailsUI" href="{{ '/content_hosts?search=' + createSearchString('applicable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
@@ -30,7 +30,7 @@
30
30
 
31
31
  <dt translate>Upgradable For</dt>
32
32
  <dd>
33
- <a ng-if="newHostDetailsUI" href="{{ '/hosts?search=' + createSearchString('upgradable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
33
+ <a ng-if="newHostDetailsUI" href="{{ newHostUrl('upgradable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
34
34
  {{ deb.hosts_available_count }} Host(s)
35
35
  </a>
36
36
  <a ng-if="!newHostDetailsUI" href="{{ '/content_hosts?search=' + createSearchString('upgradable_debs') }}" target="_blank" rel="noreferrer noopener" translate>
@@ -8,13 +8,14 @@
8
8
  * @requires CurrentOrganization
9
9
  * @requires ApiErrorHandler
10
10
  * @requires newHostDetailsUI
11
+ * @requires newHostsPage
11
12
  *
12
13
  * @description
13
14
  * Provides the functionality for the package page.
14
15
  */
15
16
  angular.module('Bastion.packages').controller('PackageController',
16
- ['$scope', 'Package', 'Host', 'CurrentOrganization', 'ApiErrorHandler', 'newHostDetailsUI', 'newHostDetailsUI',
17
- function ($scope, Package, Host, CurrentOrganization, ApiErrorHandler, newHostDetailsUI) {
17
+ ['$scope', 'Package', 'Host', 'CurrentOrganization', 'ApiErrorHandler', 'newHostDetailsUI', 'newHostsPage',
18
+ function ($scope, Package, Host, CurrentOrganization, ApiErrorHandler, newHostDetailsUI, newHostsPage) {
18
19
  $scope.panel = {
19
20
  error: false,
20
21
  loading: true
@@ -26,6 +27,7 @@ angular.module('Bastion.packages').controller('PackageController',
26
27
 
27
28
  $scope.installedPackageCount = undefined;
28
29
  $scope.newHostDetailsUI = (newHostDetailsUI === 'true');
30
+ $scope.newHostsPage = (newHostsPage === 'true');
29
31
 
30
32
  $scope.fetchHostCount = function() {
31
33
  Host.get({'per_page': 0, 'search': $scope.createSearchString('installed_package'), 'organization_id': CurrentOrganization}, function (data) {
@@ -43,7 +45,8 @@ angular.module('Bastion.packages').controller('PackageController',
43
45
  };
44
46
 
45
47
  $scope.newHostUrl = function(field) {
46
- return '/hosts?search=' + encodeURIComponent($scope.createSearchString(field));
48
+ var basePath = $scope.newHostsPage ? '/new/hosts' : '/hosts';
49
+ return basePath + '?search=' + encodeURIComponent($scope.createSearchString(field));
47
50
  };
48
51
 
49
52
  $scope.package = Package.get({id: $scope.$stateParams.packageId}, function () {
@@ -1,3 +1,3 @@
1
1
  module Katello
2
- VERSION = "4.20.0".freeze
2
+ VERSION = "4.20.1".freeze
3
3
  end
@@ -2,7 +2,6 @@
2
2
  _("Abstract async task")
3
3
  _("Add rolling repo clone")
4
4
  _("Bulk generate applicability for hosts")
5
- _("Clean Backend Objects")
6
5
  _("Commit upload")
7
6
  _("Copy all units")
8
7
  _("Copy content")
@@ -13,7 +12,6 @@ _("Create Container Push Repository Root")
13
12
  _("Create Export History")
14
13
  _("Create Import History")
15
14
  _("Create Syncable Export History")
16
- _("Create content view")
17
15
  _("Create exporter")
18
16
  _("Create import")
19
17
  _("Create importer")
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
@@ -51,7 +51,7 @@ const AddBookmarkModal = ({ selectedItem, onClose, controller }) => {
51
51
  </FormGroup>
52
52
  <FormGroup label={__('Search Query')} isRequired fieldId="query">
53
53
  <TextInput
54
- ouiaId="query-inout"
54
+ ouiaId="query-input"
55
55
  isRequired
56
56
  type="text"
57
57
  id="query"
@@ -403,11 +403,11 @@ export const ErrataTab = () => {
403
403
  </>
404
404
  ) : null;
405
405
 
406
- const hostIsNonLibrary = (
407
- (contentFacet?.contentViewDefault === false ||
408
- contentFacet.lifecycleEnvironmentLibrary === false) &&
409
- contentFacet.contentView.rolling === false
410
- );
406
+ // Show the Applicable/Installable toggle when not all errata are installable.
407
+ // contentViewEnvironmentsAllDefaultOrRolling is true when all CVEnvs are Library or rolling,
408
+ // meaning all applicable errata are installable and the toggle is unnecessary.
409
+ const showApplicableInstallableToggle =
410
+ contentFacet?.contentViewEnvironmentsAllDefaultOrRolling === false;
411
411
  const toggleGroup = (
412
412
  <Split hasGutter>
413
413
  <SplitItem>
@@ -432,7 +432,7 @@ export const ErrataTab = () => {
432
432
  isDisabled={!results?.length}
433
433
  />
434
434
  </SplitItem>
435
- {hostIsNonLibrary &&
435
+ {showApplicableInstallableToggle &&
436
436
  <SplitItem>
437
437
  <ToggleGroup aria-label="Installable Errata">
438
438
  <ToggleGroupItem
@@ -510,7 +510,7 @@ export const ErrataTab = () => {
510
510
  displaySelectAllCheckbox={showActions}
511
511
  requestKey={HOST_ERRATA_KEY}
512
512
  alwaysShowActionButtons={false}
513
- alwaysShowToggleGroup={hostIsNonLibrary && neededErrata}
513
+ alwaysShowToggleGroup={showApplicableInstallableToggle && neededErrata}
514
514
  >
515
515
  <Thead>
516
516
  <Tr ouiaId="row-header">
@@ -19,11 +19,7 @@ jest.mock('../../hostDetailsHelpers', () => ({
19
19
  const contentFacetAttributes = {
20
20
  id: 11,
21
21
  uuid: 'e5761ea3-4117-4ecf-83d0-b694f99b389e',
22
- content_view_default: false,
23
- contentView: {
24
- rolling: false,
25
- },
26
- lifecycle_environment_library: false,
22
+ content_view_environments_all_default_or_rolling: false,
27
23
  errata_counts: {
28
24
  total: 3,
29
25
  },
@@ -572,139 +568,45 @@ test('Select all is disabled if all rows are selected', async (done) => {
572
568
  done(); // Pass jest callback to confirm test is done
573
569
  });
574
570
 
575
- test('Toggle Group shows if it\'s not the default content view or library enviroment', async (done) => {
576
- // Setup autocomplete with mockForemanAutoComplete since we aren't adding /katello
577
- const autocompleteScope = mockForemanAutocomplete(nockInstance, autocompleteUrl);
578
- const mockErrata = makeMockErrata({});
579
- // return errata data results when we look for errata
580
- const scope = nockInstance
581
- .get(hostErrata)
582
- .query(defaultQuery)
583
- .reply(200, mockErrata);
584
-
585
- const {
586
- queryByLabelText,
587
- getAllByText,
588
- } = renderWithRedux(<ErrataTab />, renderOptions(cfWithErrataTotal(mockErrata.total)));
589
-
590
- // Assert that the errata are now showing on the screen, but wait for them to appear.
591
- await patientlyWaitFor(() => expect(getAllByText('Important')[0]).toBeInTheDocument());
592
- expect(queryByLabelText('Installable Errata')).toBeInTheDocument();
593
- assertNockRequest(autocompleteScope);
594
- assertNockRequest(scope);
595
- done(); // Pass jest callback to confirm test is done
596
- });
597
-
598
- test('Toggle Group shows if it\'s the default content view but non-library environment', async (done) => {
599
- // Setup autocomplete with mockForemanAutoComplete since we aren't adding /katello
571
+ test('Toggle Group shows when content_view_environments_all_default_or_rolling is false', async (done) => {
600
572
  const autocompleteScope = mockForemanAutocomplete(nockInstance, autocompleteUrl);
601
573
  const mockErrata = makeMockErrata({});
602
574
  const options = renderOptions({
603
575
  ...cfWithErrataTotal(mockErrata.total),
604
- content_view_default: true,
576
+ content_view_environments_all_default_or_rolling: false,
605
577
  });
606
- // return errata data results when we look for errata
607
578
  const scope = nockInstance
608
579
  .get(hostErrata)
609
580
  .query(defaultQuery)
610
581
  .reply(200, mockErrata);
611
582
 
612
- const {
613
- queryByLabelText,
614
- getAllByText,
615
- } = renderWithRedux(<ErrataTab />, options);
583
+ const { queryByLabelText, getAllByText } = renderWithRedux(<ErrataTab />, options);
616
584
 
617
- // Assert that the errata are now showing on the screen, but wait for them to appear.
618
585
  await patientlyWaitFor(() => expect(getAllByText('Important')[0]).toBeInTheDocument());
619
586
  expect(queryByLabelText('Installable Errata')).toBeInTheDocument();
620
587
  assertNockRequest(autocompleteScope);
621
588
  assertNockRequest(scope);
622
- done(); // Pass jest callback to confirm test is done
623
- });
624
-
625
- test('Toggle Group shows if it\'s the library environment but non-default content view', async (done) => {
626
- // Setup autocomplete with mockForemanAutoComplete since we aren't adding /katello
627
- const autocompleteScope = mockForemanAutocomplete(nockInstance, autocompleteUrl);
628
- const mockErrata = makeMockErrata({});
629
- const options = renderOptions({
630
- ...cfWithErrataTotal(mockErrata.total),
631
- lifecycle_environment_library: true,
632
- });
633
-
634
- // return errata data results when we look for errata
635
- const scope = nockInstance
636
- .get(hostErrata)
637
- .query(defaultQuery)
638
- .reply(200, mockErrata);
639
-
640
- const {
641
- queryByLabelText,
642
- getAllByText,
643
- } = renderWithRedux(<ErrataTab />, options);
644
-
645
- // Assert that the errata are now showing on the screen, but wait for them to appear.
646
- await patientlyWaitFor(() => expect(getAllByText('Important')[0]).toBeInTheDocument());
647
- expect(queryByLabelText('Installable Errata')).toBeInTheDocument();
648
- assertNockRequest(autocompleteScope);
649
- assertNockRequest(scope);
650
- done(); // Pass jest callback to confirm test is done
651
- });
652
-
653
- test('Toggle Group does not show if it\'s the default content view and library environment', async (done) => {
654
- // Setup autocomplete with mockForemanAutoComplete since we aren't adding /katello
655
- const autocompleteScope = mockForemanAutocomplete(nockInstance, autocompleteUrl);
656
- const mockErrata = makeMockErrata({});
657
- const options = renderOptions({
658
- ...cfWithErrataTotal(mockErrata.total),
659
- content_view_default: true,
660
- lifecycle_environment_library: true,
661
- });
662
- // return errata data results when we look for errata
663
- const scope = nockInstance
664
- .get(hostErrata)
665
- .query(defaultQuery)
666
- .reply(200, mockErrata);
667
-
668
- const {
669
- queryByLabelText,
670
- getAllByText,
671
- } = renderWithRedux(<ErrataTab />, options);
672
-
673
- // Assert that the errata are now showing on the screen, but wait for them to appear.
674
- await patientlyWaitFor(() => expect(getAllByText('Important')[0]).toBeInTheDocument());
675
- expect(queryByLabelText('Installable Errata')).not.toBeInTheDocument();
676
- assertNockRequest(autocompleteScope);
677
- assertNockRequest(scope);
678
- done(); // Pass jest callback to confirm test is done
589
+ done();
679
590
  });
680
591
 
681
- test('Toggle Group does not show if it\'s a rolling content view and library environment', async (done) => {
682
- // Setup autocomplete with mockForemanAutoComplete since we aren't adding /katello
592
+ test('Toggle Group does not show when content_view_environments_all_default_or_rolling is true', async (done) => {
683
593
  const autocompleteScope = mockForemanAutocomplete(nockInstance, autocompleteUrl);
684
594
  const mockErrata = makeMockErrata({});
685
595
  const options = renderOptions({
686
596
  ...cfWithErrataTotal(mockErrata.total),
687
- contentView: {
688
- rolling: true,
689
- },
690
- lifecycle_environment_library: true,
597
+ content_view_environments_all_default_or_rolling: true,
691
598
  });
692
- // return errata data results when we look for errata
693
599
  const scope = nockInstance
694
600
  .get(hostErrata)
695
601
  .query(defaultQuery)
696
602
  .reply(200, mockErrata);
697
603
 
698
- const {
699
- queryByLabelText,
700
- getAllByText,
701
- } = renderWithRedux(<ErrataTab />, options);
604
+ const { queryByLabelText, getAllByText } = renderWithRedux(<ErrataTab />, options);
702
605
 
703
- // Assert that the errata are now showing on the screen, but wait for them to appear.
704
606
  await patientlyWaitFor(() => expect(getAllByText('Important')[0]).toBeInTheDocument());
705
607
  expect(queryByLabelText('Installable Errata')).not.toBeInTheDocument();
706
608
  assertNockRequest(autocompleteScope);
707
- assertNockRequest(scope, done); // Pass jest callback to confirm test is done
609
+ assertNockRequest(scope, done);
708
610
  });
709
611
 
710
612
  test('Selection is disabled for errata which are applicable but not installable', async (done) => {
@@ -33,9 +33,9 @@
33
33
  "ssl_key_products": [],
34
34
  "ssl_key_root_repos": [],
35
35
  "permissions": {
36
- "view_content_credenials": true,
37
- "edit_content_credenials": true,
38
- "destroy_content_credenials": true
36
+ "view_content_credentials": true,
37
+ "edit_content_credentials": true,
38
+ "destroy_content_credentials": true
39
39
  }
40
40
  },
41
41
  {
@@ -60,9 +60,9 @@
60
60
  "ssl_key_products": [],
61
61
  "ssl_key_root_repos": [],
62
62
  "permissions": {
63
- "view_content_credenials": true,
64
- "edit_content_credenials": true,
65
- "destroy_content_credenials": true
63
+ "view_content_credentials": true,
64
+ "edit_content_credentials": true,
65
+ "destroy_content_credentials": true
66
66
  }
67
67
  }
68
68
  ]
@@ -104,7 +104,7 @@ const BootedContainerImagesPage = () => {
104
104
  <TableIndexPage
105
105
  apiUrl={BOOTED_CONTAINER_IMAGES_API_PATH}
106
106
  apiOptions={apiOptions}
107
- createable={false}
107
+ creatable={false}
108
108
  isDeleteable={false}
109
109
  controller="/katello/api/v2/host_bootc_images"
110
110
  >
@@ -35,9 +35,9 @@ const contentCredentialsResponse = Immutable({
35
35
  ssl_key_products: [],
36
36
  ssl_key_root_repos: [],
37
37
  permissions: {
38
- view_content_credenials: true,
39
- edit_content_credenials: true,
40
- destroy_content_credenials: true,
38
+ view_content_credentials: true,
39
+ edit_content_credentials: true,
40
+ destroy_content_credentials: true,
41
41
  },
42
42
  },
43
43
  {
@@ -62,9 +62,9 @@ const contentCredentialsResponse = Immutable({
62
62
  ssl_key_products: [],
63
63
  ssl_key_root_repos: [],
64
64
  permissions: {
65
- view_content_credenials: true,
66
- edit_content_credenials: true,
67
- destroy_content_credenials: true,
65
+ view_content_credentials: true,
66
+ edit_content_credentials: true,
67
+ destroy_content_credentials: true,
68
68
  },
69
69
  },
70
70
  ],
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katello
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.20.0
4
+ version: 4.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - N/A
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-11 00:00:00.000000000 Z
11
+ date: 2026-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -5624,7 +5624,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
5624
5624
  - !ruby/object:Gem::Version
5625
5625
  version: '0'
5626
5626
  requirements: []
5627
- rubygems_version: 3.1.6
5627
+ rubygems_version: 3.2.33
5628
5628
  signing_key:
5629
5629
  specification_version: 4
5630
5630
  summary: Content and Subscription Management plugin for Foreman