katello 3.15.0.rc1.1 → 3.15.0.rc1.2

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.

Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/hosts/activation_key_edit.js +32 -54
  3. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
  4. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +11 -4
  5. data/app/controllers/katello/api/v2/repositories_controller.rb +7 -3
  6. data/app/controllers/katello/api/v2/subscriptions_controller.rb +2 -3
  7. data/app/lib/actions/katello/content_view/incremental_updates.rb +1 -1
  8. data/app/lib/actions/katello/host/erratum/applicable_errata_install.rb +6 -2
  9. data/app/lib/actions/pulp3/content_migration.rb +5 -5
  10. data/app/lib/actions/pulp3/import_migration.rb +3 -3
  11. data/app/lib/actions/pulp3/orchestration/repository/import_upload.rb +1 -1
  12. data/app/lib/katello/concerns/base_template_scope_extensions.rb +1 -1
  13. data/app/models/katello/authorization/pool.rb +5 -1
  14. data/app/models/katello/authorization/subscription.rb +3 -1
  15. data/app/models/katello/candlepin/repository_mapper.rb +5 -1
  16. data/app/models/katello/concerns/host_managed_extensions.rb +17 -8
  17. data/app/models/katello/concerns/pulp_database_unit.rb +1 -1
  18. data/app/models/katello/content_view.rb +4 -0
  19. data/app/models/katello/glue/candlepin/candlepin_object.rb +0 -6
  20. data/app/models/katello/glue/candlepin/owner.rb +1 -1
  21. data/app/models/katello/glue/candlepin/repository.rb +0 -8
  22. data/app/models/katello/glue/pulp/repos.rb +41 -1
  23. data/app/models/katello/installed_package.rb +5 -4
  24. data/app/models/katello/pool.rb +1 -1
  25. data/app/services/katello/pulp/repository/docker.rb +12 -2
  26. data/app/services/katello/pulp/simple_package.rb +6 -2
  27. data/app/services/katello/pulp3/erratum.rb +4 -0
  28. data/app/services/katello/pulp3/migration.rb +61 -8
  29. data/app/services/katello/pulp3/migration_plan.rb +79 -5
  30. data/app/views/dashboard/_subscription_widget.html.erb +1 -1
  31. data/app/views/katello/api/v2/organizations/show.json.rabl +3 -0
  32. data/app/views/katello/api/v2/packages/backend.json.rabl +4 -0
  33. data/app/views/katello/api/v2/products/show.json.rabl +2 -0
  34. data/app/views/overrides/activation_keys/_host_tab_pane.html.erb +10 -2
  35. data/app/views/smart_proxies/plugins/{_pulp3.html.erb → _pulpcore.html.erb} +0 -0
  36. data/config/katello.yaml.example +0 -6
  37. data/db/migrate/20200121213430_katello_repository_rpms_id_big_int.rb +5 -0
  38. data/db/migrate/20200129172534_add_epoch_version_release_arch_to_katello_installed_packages.rb +40 -0
  39. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -1
  40. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-errata-modal.controller.js +9 -0
  41. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-subscriptions-modal.controller.js +4 -4
  42. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +2 -0
  43. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-subscriptions-modal.html +3 -3
  44. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content-host-modal-helper.service.js +1 -0
  45. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-details.controller.js +4 -0
  46. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-subscriptions.controller.js +4 -2
  47. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-info.html +2 -2
  48. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-subscriptions.html +15 -10
  49. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/packages/details/views/package-info.html +1 -1
  50. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/content-access-mode-banner.directive.js +4 -4
  51. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/views/content-access-mode-banner.html +1 -1
  52. data/lib/katello/tasks/pulp3_migration.rake +1 -1
  53. data/lib/katello/tasks/pulp3_post_migration_check.rake +32 -0
  54. data/lib/katello/tasks/repository.rake +10 -7
  55. data/lib/katello/version.rb +1 -1
  56. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  57. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalSelectors.js +2 -0
  58. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +4 -0
  59. data/webpack/scenes/Organizations/OrganizationSelectors.js +4 -0
  60. data/webpack/scenes/Subscriptions/Manifest/DeleteManifestModalText.js +14 -9
  61. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +183 -169
  62. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.scss +12 -0
  63. data/webpack/scenes/Subscriptions/Manifest/ManifestConstants.js +4 -0
  64. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +5 -3
  65. data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +130 -181
  66. data/webpack/scenes/Subscriptions/Manifest/index.js +10 -1
  67. data/webpack/scenes/Subscriptions/SubscriptionActions.js +4 -9
  68. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +0 -3
  69. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +3 -11
  70. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +33 -13
  71. data/webpack/scenes/Subscriptions/SubscriptionsSelectors.js +2 -5
  72. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsActions.test.js +0 -7
  73. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +5 -0
  74. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +0 -12
  75. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsSelectors.test.js +0 -3
  76. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsActions.test.js.snap +0 -12
  77. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +2 -3
  78. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsReducer.test.js.snap +0 -73
  79. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsSelectors.test.js.snap +0 -3
  80. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +0 -4
  81. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +13 -22
  82. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +10 -0
  83. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +2 -1
  84. data/webpack/scenes/Subscriptions/index.js +4 -3
  85. data/webpack/scenes/Tasks/TaskConstants.js +1 -0
  86. metadata +41 -18
  87. data/app/assets/javascripts/katello/widgets/auto_complete.js +0 -183
  88. data/db/seeds.d/115-http_proxy.rb +0 -25
  89. data/vendor/assets/stylesheets/katello/jquery.jnotify.css +0 -98
@@ -0,0 +1,5 @@
1
+ class KatelloRepositoryRpmsIdBigInt < ActiveRecord::Migration[5.2]
2
+ def change
3
+ change_column :katello_repository_rpms, :id, :bigint
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ class AddEpochVersionReleaseArchToKatelloInstalledPackages < ActiveRecord::Migration[5.2]
2
+ def up
3
+ add_column :katello_installed_packages, :nvrea, :string
4
+ add_column :katello_installed_packages, :epoch, :string
5
+ add_column :katello_installed_packages, :version, :string
6
+ add_column :katello_installed_packages, :release, :string
7
+ add_column :katello_installed_packages, :arch, :string
8
+
9
+ epoch_non_0 = ::Katello::Rpm.where.not(epoch: [0, nil]).pluck(:nvra, :epoch).to_h
10
+ installed_packages = []
11
+
12
+ ::Katello::InstalledPackage.reset_column_information
13
+ ::Katello::InstalledPackage.find_each do |pkg|
14
+ epoch = epoch_non_0[pkg.nvra] || "0"
15
+
16
+ attributes_hash = ::Katello::Util::Package.parse_nvrea(pkg.nvra)
17
+ attributes_hash[:epoch] = epoch
18
+ attributes_hash[:nvra] = pkg.nvra
19
+ if epoch == "0"
20
+ attributes_hash[:nvrea] = pkg.nvra
21
+ else
22
+ attributes_hash[:nvrea] = "#{pkg.name}-#{epoch}:#{attributes_hash[:version]}-"\
23
+ "#{attributes_hash[:release]}.#{attributes_hash[:arch]}"
24
+ end
25
+
26
+ installed_packages << ::Katello::InstalledPackage.new(attributes_hash)
27
+ end
28
+ ::Katello::InstalledPackage.import(installed_packages, validate: false, batch_size: 50_000,
29
+ on_duplicate_key_update: {conflict_target: [:nvra],
30
+ columns: [:nvrea, :epoch, :version, :release, :arch]})
31
+ end
32
+
33
+ def down
34
+ remove_column :katello_installed_packages, :nvrea, :string
35
+ remove_column :katello_installed_packages, :epoch, :string
36
+ remove_column :katello_installed_packages, :version, :string
37
+ remove_column :katello_installed_packages, :release, :string
38
+ remove_column :katello_installed_packages, :arch, :string
39
+ end
40
+ end
@@ -18,7 +18,7 @@
18
18
  angular.module('Bastion.features').value('FeatureSettings', angular.fromJson(<%= SETTINGS[:features].nil? ? {} : SETTINGS[:features].to_json.html_safe %>));
19
19
  angular.module('Bastion').value('currentLocale', '<%= I18n.locale %>');
20
20
  angular.module('Bastion').value('CurrentOrganization', "<%= Organization.current.id if Organization.current %>");
21
- angular.module('Bastion').value('contentAccessMode', "<%= Organization.current.try(:content_access_mode) if Organization.current %>");
21
+ angular.module('Bastion').value('simpleContentAccessEnabled', <%= Organization.current.simple_content_access? if Organization.current %>);
22
22
  angular.module('Bastion').value('foreman', tfm);
23
23
  angular.module('Bastion').value('repositoryTypes', angular.fromJson('<%= Katello::RepositoryTypeManager.repository_types.values.to_json.html_safe %>'));
24
24
  angular.module('Bastion').value('deleteHostOnUnregister', angular.fromJson('<%= Setting[:unregister_delete_host] %>'));
@@ -29,6 +29,13 @@ angular.module('Bastion.content-hosts').controller('ContentHostsBulkErrataModalC
29
29
  var params = hostIds;
30
30
  params['content_type'] = 'errata';
31
31
  params.content = _.map($scope.table.getSelected(), 'errata_id');
32
+
33
+ if (nutupane.table.allResultsSelected) {
34
+ params['install_all'] = true;
35
+ } else {
36
+ params['install_all'] = false;
37
+ }
38
+
32
39
  params['organization_id'] = CurrentOrganization;
33
40
  return params;
34
41
  }
@@ -38,6 +45,8 @@ angular.module('Bastion.content-hosts').controller('ContentHostsBulkErrataModalC
38
45
  }
39
46
 
40
47
  nutupane = new Nutupane(HostBulkAction, hostIds, 'installableErrata');
48
+ nutupane.enableSelectAllResults();
49
+
41
50
  $scope.controllerName = 'katello_errata';
42
51
  nutupane.masterOnly = true;
43
52
  $scope.showErrata = false;
@@ -12,13 +12,14 @@
12
12
  * @requires SubscriptionsHelper
13
13
  * @requires Notification
14
14
  * @requires hostIds
15
+ * @requires simpleContentAccessEnabled
15
16
  *
16
17
  * @description
17
18
  * A controller for providing bulk action functionality to the content hosts page.
18
19
  */
19
20
  angular.module('Bastion.content-hosts').controller('ContentHostsBulkSubscriptionsModalController',
20
- ['$scope', '$location', '$uibModalInstance', 'Nutupane', 'CurrentOrganization', 'HostBulkAction', 'Subscription', 'SubscriptionsHelper', 'Notification', 'hostIds', 'contentAccessMode',
21
- function ($scope, $location, $uibModalInstance, Nutupane, CurrentOrganization, HostBulkAction, Subscription, SubscriptionsHelper, Notification, hostIds, contentAccessMode) {
21
+ ['$scope', '$location', '$uibModalInstance', 'Nutupane', 'CurrentOrganization', 'HostBulkAction', 'Subscription', 'SubscriptionsHelper', 'Notification', 'hostIds', 'simpleContentAccessEnabled',
22
+ function ($scope, $location, $uibModalInstance, Nutupane, CurrentOrganization, HostBulkAction, Subscription, SubscriptionsHelper, Notification, hostIds, simpleContentAccessEnabled) {
22
23
  var success, error, params = {
23
24
  'organization_id': CurrentOrganization,
24
25
  'sort_order': 'ASC',
@@ -44,7 +45,6 @@ angular.module('Bastion.content-hosts').controller('ContentHostsBulkSubscription
44
45
  });
45
46
  };
46
47
 
47
-
48
48
  $scope.contentNutupane = new Nutupane(Subscription, params,
49
49
  'queryPaged', {disableAutoLoad: true});
50
50
  $scope.controllerName = 'katello_subscriptions';
@@ -53,7 +53,7 @@ angular.module('Bastion.content-hosts').controller('ContentHostsBulkSubscription
53
53
  $scope.contentNutupane.masterOnly = true;
54
54
  $scope.contentNutupane.load();
55
55
  $scope.groupedSubscriptions = {};
56
- $scope.contentAccessMode = contentAccessMode;
56
+ $scope.simpleContentAccessEnabled = simpleContentAccessEnabled;
57
57
 
58
58
  $scope.$watch('table.rows', function (rows) {
59
59
  $scope.groupedSubscriptions = SubscriptionsHelper.groupByProductName(rows);
@@ -64,6 +64,8 @@
64
64
  </span>
65
65
 
66
66
  <div data-block="table">
67
+ <div data-extend-template="layouts/select-all-results.html"></div>
68
+
67
69
  <table class="table table-striped table-bordered" ng-class="{'table-mask': table.working}">
68
70
  <thead>
69
71
  <tr bst-table-head row-select>
@@ -7,11 +7,11 @@
7
7
 
8
8
  <div class="row">
9
9
  <div class="col-sm-12">
10
- <button type="button" class="btn btn-default" ng-click="autoAttach()" ng-disabled="table.numSelected > 0 || contentAccessMode === 'org_environment'">
10
+ <button type="button" class="btn btn-default" ng-click="autoAttach()" ng-disabled="table.numSelected > 0 || simpleContentAccessEnabled">
11
11
  <span translate>Auto-Attach</span>
12
12
  </button>
13
13
 
14
- <p class="help-text" ng-show="table.numSelected === 0 && contentAccessMode != 'org_environment'">
14
+ <p class="help-text" ng-show="table.numSelected === 0 && !simpleContentAccessEnabled">
15
15
  Auto-attach available subscriptions to all selected hosts.
16
16
  </p>
17
17
 
@@ -19,7 +19,7 @@
19
19
  <div content-access-mode-banner/>
20
20
  </p>
21
21
 
22
- <p class="help-text" ng-show="table.numSelected > 0 && contentAccessMode != 'org_environment'">
22
+ <p class="help-text" ng-show="table.numSelected > 0 && !simpleContentAccessEnabled">
23
23
  Auto-attach uses all available subscriptions, not a selected subset.
24
24
  </p>
25
25
  </div>
@@ -39,6 +39,7 @@ angular.module('Bastion.content-hosts').service('ContentHostsModalHelper', ['$ui
39
39
  $uibModal.open({
40
40
  templateUrl: 'content-hosts/bulk/views/content-hosts-bulk-errata-modal.html',
41
41
  controller: 'ContentHostsBulkErrataModalController',
42
+ openedClass: 'bastion',
42
43
  size: 'lg',
43
44
  resolve: {
44
45
  hostIds: this.resolveFunc()
@@ -141,6 +141,10 @@ angular.module('Bastion.content-hosts').controller('ContentHostDetailsController
141
141
  return false;
142
142
  };
143
143
 
144
+ $scope.autoHealOptions = function () {
145
+ return [{value: true, name: translate("Yes")}, {value: true, name: translate("No")}];
146
+ };
147
+
144
148
  $scope.serviceLevels = function () {
145
149
  return $scope.organization.$promise.then(function(org) {
146
150
  return _.union(org.service_levels, $scope.defaultServiceLevels);
@@ -8,13 +8,14 @@
8
8
  * @requires Subscription
9
9
  * @requires SubscriptionsHelper
10
10
  * @requires Notification
11
+ * @requires simpleContentAccessEnabled
11
12
  *
12
13
  * @description
13
14
  * Provides the functionality for the content host details action pane.
14
15
  */
15
16
  angular.module('Bastion.content-hosts').controller('ContentHostSubscriptionsController',
16
- ['$scope', '$location', 'translate', 'Nutupane', 'CurrentOrganization', 'Subscription', 'Host', 'HostSubscription', 'SubscriptionsHelper', 'Notification',
17
- function ($scope, $location, translate, Nutupane, CurrentOrganization, Subscription, Host, HostSubscription, SubscriptionsHelper, Notification) {
17
+ ['$scope', '$location', 'translate', 'Nutupane', 'CurrentOrganization', 'Subscription', 'Host', 'HostSubscription', 'SubscriptionsHelper', 'Notification', 'simpleContentAccessEnabled',
18
+ function ($scope, $location, translate, Nutupane, CurrentOrganization, Subscription, Host, HostSubscription, SubscriptionsHelper, Notification, simpleContentAccessEnabled) {
18
19
 
19
20
  var params = {
20
21
  'organization_id': CurrentOrganization,
@@ -30,6 +31,7 @@ angular.module('Bastion.content-hosts').controller('ContentHostSubscriptionsCont
30
31
  $scope.nutupane.masterOnly = true;
31
32
  $scope.isRemoving = false;
32
33
  $scope.contextAdd = false;
34
+ $scope.simpleContentAccessEnabled = simpleContentAccessEnabled;
33
35
  $scope.groupedSubscriptions = {};
34
36
  $scope.$watch('table.rows', function (rows) {
35
37
  $scope.groupedSubscriptions = SubscriptionsHelper.groupByProductName(rows);
@@ -82,12 +82,12 @@
82
82
  </dd>
83
83
 
84
84
  <dt translate>Auto-Attach</dt>
85
- <dd bst-edit-checkbox="host.subscription_facet_attributes.autoheal"
85
+ <dd ng-if= "simpleContentAccessEnabled" translate> Not Applicable </dd>
86
+ <dd ng-if= "!simpleContentAccessEnabled" bst-edit-select="host.subscription_facet_attributes.autoheal"
86
87
  readonly="denied('edit_hosts', host)"
87
88
  formatter="booleanToYesNo"
88
89
  on-save="saveSubscriptionFacet(host)">
89
90
  </dd>
90
-
91
91
  </dl>
92
92
 
93
93
  <div class="divider"></div>
@@ -25,22 +25,27 @@
25
25
 
26
26
  <dt translate>Auto-Attach</dt>
27
27
  <dd>
28
+ <span ng-if="simpleContentAccessEnabled" translate>
29
+ Not Applicable
30
+ </span>
31
+ <div ng-if="!simpleContentAccessEnabled">
28
32
  <div bst-edit-checkbox="host.subscription_facet_attributes.autoheal"
29
33
  formatter="booleanToYesNo"
30
34
  readonly="denied('edit_hosts', host)"
31
35
  on-save="saveSubscriptionFacet(host)">
32
36
  </div>
37
+ <a ng-hide="denied('edit_hosts', host)"
38
+ ng-click="autoAttachSubscriptions()"
39
+ class="btn btn-default"
40
+ ng-disabled="subscription.workingMode">
41
+ <span translate>Run Auto-Attach</span>
42
+ </a>
33
43
 
34
- <a ng-hide="denied('edit_hosts', host)"
35
- ng-click="autoAttachSubscriptions()"
36
- ng-disabled="subscription.workingMode">
37
- <span translate>Run Auto-Attach</span>
38
- </a>
39
-
40
- <span ng-show="subscription.workingMode">
41
- <i class="fa fa-spinner inline-icon fa-spin"></i>
42
- <span translate>Working</span>
43
- </span>
44
+ <span ng-show="subscription.workingMode">
45
+ <i class="fa fa-spinner inline-icon fa-spin"></i>
46
+ <span translate>Working</span>
47
+ </span>
48
+ </div>
44
49
  </dd>
45
50
 
46
51
  <dt translate>Service Level</dt>
@@ -79,7 +79,7 @@
79
79
  <dd>{{ package.buildhost }}</dd>
80
80
 
81
81
  <dt translate>Build Time</dt>
82
- <dd>{{ package.build_time }}</dd>
82
+ <dd><long-date-time date="package.build_time_utc" /></dd>
83
83
  </dl>
84
84
  </div>
85
85
  </div>
@@ -2,19 +2,19 @@
2
2
  * @ngdoc directive
3
3
  * @name Bastion.subscriptions:contentAccessModeBanner
4
4
  *
5
- * @requires contentAccessMode
5
+ * @requires simpleContentAccessEnabled
6
6
  *
7
7
  * @description
8
8
  * Component for showing information about content access mode (whether content is
9
9
  * allowed with or without a subscription)
10
10
  */
11
11
  angular.module('Bastion.subscriptions').directive('contentAccessModeBanner',
12
- ['contentAccessMode',
13
- function (contentAccessMode) {
12
+ ['simpleContentAccessEnabled',
13
+ function (simpleContentAccessEnabled) {
14
14
  return {
15
15
  restrict: 'AE',
16
16
  controller: ['$scope', function ($scope) {
17
- $scope.contentAccessMode = contentAccessMode;
17
+ $scope.simpleContentAccessEnabled = simpleContentAccessEnabled;
18
18
  }],
19
19
  templateUrl: 'subscriptions/views/content-access-mode-banner.html'
20
20
  };
@@ -1,4 +1,4 @@
1
- <div bst-alert="info" ng-show="contentAccessMode === 'org_environment'">
1
+ <div bst-alert="info" ng-show="simpleContentAccessEnabled">
2
2
  <span translate>
3
3
  This organization has Simple Content Access enabled. Hosts can consume from all repositories in their Content View regardless of subscription status.
4
4
  </span>
@@ -1,7 +1,7 @@
1
1
  namespace :katello do
2
2
  desc "Runs a Pulp 2 to 3 Content Migration for supported types. May be run multiple times. Use wait=false to immediately return with a task url."
3
3
  task :pulp3_migration => ["environment", "disable_dynflow", "check_ping"] do
4
- task = ForemanTasks.async_task(Actions::Pulp3::ContentMigration, [Katello::Repository::FILE_TYPE])
4
+ task = ForemanTasks.async_task(Actions::Pulp3::ContentMigration)
5
5
 
6
6
  if ENV['wait'].nil? || ::Foreman::Cast.to_bool(ENV['wait'])
7
7
  until !task.pending? || task.paused?
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../engine", File.dirname(__FILE__))
2
+
3
+ namespace :katello do
4
+ desc "Runs a post Pulp3 migration check for supported content types."
5
+ task :pulp3_post_migration_check => :environment do
6
+ repository_types = Katello::Pulp3::Migration::REPOSITORY_TYPES
7
+
8
+ repository_types.each do |type|
9
+ filter = 'version_href is NULL OR remote_href is NULL'
10
+
11
+ unless type == Katello::Repository::DOCKER_TYPE
12
+ filter += ' OR publication_href is NULL'
13
+ end
14
+ repositories = Katello::Repository.with_type(type).where(filter)
15
+
16
+ if repositories.any?
17
+ $stderr.print("ERROR: #{type} repository #{repositories.first.id} has a NULL value for remote_href, version_href, publication_href\n")
18
+ exit 1
19
+ end
20
+
21
+ non_archived_repositories = Katello::Repository.with_type(type).non_archived
22
+ with_no_distribution_references = non_archived_repositories
23
+ .left_outer_joins(:distribution_references)
24
+ .where(katello_distribution_references: { id: nil })
25
+
26
+ if with_no_distribution_references.any?
27
+ $stderr.print("ERROR: A non-archive #{type} repository #{with_no_distribution_references.first.id} did not have a distribution reference\n")
28
+ exit 1
29
+ end
30
+ end
31
+ end
32
+ end
@@ -11,14 +11,17 @@ namespace :katello do
11
11
  task :publish_unpublished_repositories => ["environment", "disable_dynflow", "check_ping"] do
12
12
  needing_publish = []
13
13
  Organization.find_each do |org|
14
- org.default_content_view.versions.first.repositories.where.not(:url => nil).find_each do |repo|
15
- begin
16
- if repo.needs_metadata_publish?
17
- Rails.logger.error("Repository metadata for #{repo.name} (#{repo.id}) is out of date, regenerating.")
18
- needing_publish << repo.id
14
+ if org.default_content_view && !org.default_content_view.versions.empty?
15
+ org.default_content_view.versions.first.repositories.joins(:root)
16
+ .where.not(katello_root_repositories: { url: nil }).find_each do |repo|
17
+ begin
18
+ if repo.needs_metadata_publish?
19
+ Rails.logger.error("Repository metadata for #{repo.name} (#{repo.id}) is out of date, regenerating.")
20
+ needing_publish << repo.id
21
+ end
22
+ rescue => e
23
+ puts "Failed to check repository #{repo.id}: #{e}"
19
24
  end
20
- rescue => e
21
- puts "Failed to check repository #{repo.id}: #{e}"
22
25
  end
23
26
  end
24
27
  end
@@ -1,3 +1,3 @@
1
1
  module Katello
2
- VERSION = "3.15.0.rc1.1".freeze
2
+ VERSION = "3.15.0.rc1.2".freeze
3
3
  end
@@ -0,0 +1,2 @@
1
+ const ForemanModalActions = () => jest.fn();
2
+ export default ForemanModalActions;
@@ -0,0 +1,2 @@
1
+ const ForemanModalSelectors = () => jest.fn();
2
+ export default ForemanModalSelectors;
@@ -0,0 +1,4 @@
1
+ const ForemanModal = () => jest.fn();
2
+ ForemanModal.Header = () => jest.fn();
3
+ ForemanModal.Footer = () => jest.fn();
4
+ export default ForemanModal;
@@ -0,0 +1,4 @@
1
+ export const selectOrganizationState = state => state.katello.organization;
2
+
3
+ export const selectSimpleContentAccessEnabled = state =>
4
+ selectOrganizationState(state).simple_content_access;
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { translate as __ } from 'foremanReact/common/I18n';
2
3
 
3
4
  const question = __('Are you sure you want to delete the manifest?');
@@ -11,14 +12,18 @@ const l4 = __(`Require you to upload the subscription-manifest and re-attach
11
12
  const debug = __(`This action should only be taken in extreme circumstances or
12
13
  for debugging purposes.`);
13
14
 
14
- const DeleteManifestModalText = `<p>${question}</p>
15
- <p>${note}</p>
16
- <ul class="list-aligned">
17
- <li>${l1}</li>
18
- <li>${l2}</li>
19
- <li>${l3}</li>
20
- <li>${l4}</li>
21
- </ul>
22
- <p>${debug}</p>`;
15
+ const DeleteManifestModalText = () => (
16
+ <React.Fragment>
17
+ <p>{question}</p>
18
+ <p>{note}</p>
19
+ <ul className="list-aligned">
20
+ <li>{l1}</li>
21
+ <li>{l2}</li>
22
+ <li>{l3}</li>
23
+ <li>{l4}</li>
24
+ </ul>
25
+ <p>{debug}</p>
26
+ </React.Fragment>
27
+ );
23
28
 
24
29
  export default DeleteManifestModalText;
@@ -1,7 +1,8 @@
1
1
  import React, { Component } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Col, Tabs, Tab, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
4
- import { Button, Icon, Modal, Spinner, OverlayTrigger, Tooltip, MessageDialog } from 'patternfly-react';
3
+ import { Grid, Col, Row, Tabs, Tab, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
4
+ import { Button, Spinner, OverlayTrigger, Tooltip, Icon } from 'patternfly-react';
5
+ import ForemanModal from 'foremanReact/components/ForemanModal';
5
6
  import { isEqual } from 'lodash';
6
7
  import { translate as __ } from 'foremanReact/common/I18n';
7
8
  import TooltipButton from '../../../move_to_pf/TooltipButton';
@@ -10,25 +11,24 @@ import { Table } from '../../../move_to_foreman/components/common/table';
10
11
  import { manifestExists } from '../SubscriptionHelpers';
11
12
  import { columns } from './ManifestHistoryTableSchema';
12
13
  import DeleteManifestModalText from './DeleteManifestModalText';
14
+ import { MANAGE_MANIFEST_MODAL_ID, DELETE_MANIFEST_MODAL_ID } from './ManifestConstants';
15
+
16
+ import './ManageManifestModal.scss';
13
17
 
14
18
  class ManageManifestModal extends Component {
15
19
  constructor(props) {
16
20
  super(props);
17
21
 
18
22
  this.state = {
19
- showModal: props.showModal,
20
23
  actionInProgress: props.taskInProgress,
21
- showDeleteManifestModalDialog: false,
22
24
  };
23
25
  }
24
26
 
25
27
  static getDerivedStateFromProps(newProps, prevState) {
26
28
  if (
27
- !isEqual(newProps.showModal, prevState.showModal) ||
28
29
  !isEqual(newProps.taskInProgress, prevState.actionInProgress)
29
30
  ) {
30
31
  return {
31
- showModal: newProps.showModal,
32
32
  actionInProgress: newProps.taskInProgress,
33
33
  };
34
34
  }
@@ -52,10 +52,16 @@ class ManageManifestModal extends Component {
52
52
  }
53
53
 
54
54
  hideModal = () => {
55
- this.setState({ showModal: false, showDeleteManifestModalDialog: false });
56
- this.props.onClose();
55
+ if (this.props.deleteManifestModalIsOpen) this.hideDeleteManifestModal();
56
+ this.props.setModalClosed({ id: MANAGE_MANIFEST_MODAL_ID });
57
57
  };
58
58
 
59
+ showDeleteManifestModal = () =>
60
+ this.props.setModalOpen({ id: DELETE_MANIFEST_MODAL_ID });
61
+
62
+ hideDeleteManifestModal = () =>
63
+ this.props.setModalClosed({ id: DELETE_MANIFEST_MODAL_ID });
64
+
59
65
  updateRepositoryUrl = (event) => {
60
66
  this.setState({ redhat_repository_url: event.target.value });
61
67
  };
@@ -82,13 +88,6 @@ class ManageManifestModal extends Component {
82
88
  this.hideModal();
83
89
  this.setState({ actionInProgress: true });
84
90
  this.props.delete();
85
- this.showDeleteManifestModal(false);
86
- };
87
-
88
- showDeleteManifestModal = (show) => {
89
- this.setState({
90
- showDeleteManifestModalDialog: show,
91
- });
92
91
  };
93
92
 
94
93
  disabledTooltipText = () => {
@@ -107,6 +106,7 @@ class ManageManifestModal extends Component {
107
106
  canImportManifest,
108
107
  canDeleteManifest,
109
108
  canEditOrganizations,
109
+ simpleContentAccess,
110
110
  } = this.props;
111
111
 
112
112
  const showRedHatProviderDetails = canEditOrganizations;
@@ -150,166 +150,177 @@ class ManageManifestModal extends Component {
150
150
  };
151
151
 
152
152
  return (
153
- <Modal show={this.state.showModal} onHide={this.hideModal}>
154
- <Modal.Header>
155
- <button
156
- className="close"
157
- onClick={this.hideModal}
158
- aria-label={__('Close')}
159
- >
160
- <Icon type="pf" name="close" />
161
- </button>
162
- <Modal.Title>{__('Manage Manifest')}</Modal.Title>
163
- </Modal.Header>
164
- <Modal.Body>
165
- <Tabs id="manifest-history-tabs">
166
- {showManifestTab &&
167
- <Tab eventKey={1} title={__('Manifest')}>
168
- <Form className="form-horizontal">
169
- {showRedHatProviderDetails &&
170
- <React.Fragment>
171
- <h5>{__('Red Hat Provider Details')}</h5>
172
- <hr />
173
- <FormGroup>
174
- <Col sm={3}>
175
- <ControlLabel htmlFor="cdnUrl">
176
- {__('Red Hat CDN URL')}
177
- </ControlLabel>
178
- </Col>
179
- <Col sm={9}>
180
- <FormControl
181
- id="cdnUrl"
182
- type="text"
183
- defaultValue={this.state.redhat_repository_url || organization.redhat_repository_url || ''}
184
- onBlur={this.updateRepositoryUrl}
185
- />
186
- </Col>
187
- </FormGroup>
188
- <FormGroup>
189
- <Col smOffset={3} sm={3}>
190
- <Button onClick={this.saveOrganization} disabled={organization.loading}>
191
- {organization.loading ? buttonLoading : __('Update')}
192
- </Button>
193
- </Col>
194
- </FormGroup>
195
- <br />
196
- </React.Fragment>
197
- }
198
- {showSubscriptionManifest &&
199
- <React.Fragment>
200
- <h5>{__('Subscription Manifest')}</h5>
201
- <hr />
202
-
203
- <FormGroup>
204
- <ControlLabel
205
- className="col-sm-3 control-label"
206
- htmlFor="usmaFile"
207
- style={{ paddingTop: '0' }}
208
- >
209
- <OverlayTrigger
210
- overlay={
211
- <Tooltip id="usma-tooltip">
212
- {__('Upstream Subscription Management Application')}
213
- </Tooltip>
214
- }
215
- placement="bottom"
216
- trigger={['hover', 'focus']}
217
- rootClose={false}
218
- >
219
- <span>{__('USMA')}</span>
220
- </OverlayTrigger>
221
- </ControlLabel>
222
-
223
- <Col sm={9} className="manifest-actions">
224
- <Spinner loading={actionInProgress} inline />
225
-
226
- {getManifestName()}
227
- {canImportManifest &&
153
+ <ForemanModal id={MANAGE_MANIFEST_MODAL_ID} title={__('Manage Manifest')}>
154
+ <Tabs id="manifest-history-tabs">
155
+ {showManifestTab &&
156
+ <Tab eventKey={1} title={__('Manifest')}>
157
+ <Form className="form-horizontal">
158
+ {showRedHatProviderDetails &&
159
+ <React.Fragment>
160
+ <h5>{__('Red Hat Provider Details')}</h5>
161
+ <hr />
162
+ <FormGroup>
163
+ <Grid>
164
+ <Row>
165
+ <Col sm={4}>
166
+ <ControlLabel htmlFor="cdnUrl">
167
+ {__('Red Hat CDN URL')}
168
+ </ControlLabel>
169
+ </Col>
170
+ <Col sm={8}>
228
171
  <FormControl
229
- id="usmaFile"
230
- type="file"
231
- accept=".zip"
232
- disabled={actionInProgress}
233
- onChange={e => this.uploadManifest(e.target.files)}
172
+ id="cdnUrl"
173
+ type="text"
174
+ defaultValue={this.state.redhat_repository_url || organization.redhat_repository_url || ''}
175
+ onBlur={this.updateRepositoryUrl}
234
176
  />
235
- }
236
- <div id="manifest-actions-row">
177
+ </Col>
178
+ </Row>
179
+ </Grid>
180
+ </FormGroup>
181
+ <FormGroup>
182
+ <Grid>
183
+ <Row>
184
+ <Col smOffset={4} sm={4}>
185
+ <Button onClick={this.saveOrganization} disabled={organization.loading}>
186
+ {organization.loading ? buttonLoading : __('Update')}
187
+ </Button>
188
+ </Col>
189
+ </Row>
190
+ </Grid>
191
+ </FormGroup>
192
+ <br />
193
+ </React.Fragment>
194
+ }
195
+ {showSubscriptionManifest &&
196
+ <React.Fragment>
197
+
198
+ <FormGroup>
199
+ <Grid>
200
+ <h5>{__('Subscription Manifest')}</h5>
201
+ <hr />
202
+ <Row>
203
+ <Col sm={4}>
204
+ <ControlLabel
205
+ className="control-label"
206
+ htmlFor="usmaFile"
207
+ style={{ paddingTop: '0' }}
208
+ >
209
+ <OverlayTrigger
210
+ overlay={
211
+ <Tooltip id="usma-tooltip">
212
+ {__('Upstream Subscription Management Application')}
213
+ </Tooltip>
214
+ }
215
+ placement="bottom"
216
+ trigger={['hover', 'focus']}
217
+ rootClose={false}
218
+ >
219
+ <div>{__('USMA')}</div>
220
+ </OverlayTrigger>
221
+ </ControlLabel>
222
+ </Col>
223
+ <Col sm={8}>
224
+ {getManifestName()}
225
+ </Col>
226
+ </Row>
227
+ <Row>
228
+ <Col sm={4}>
229
+ <div>{__('Simple Content Access')}</div>
230
+ </Col>
231
+ <Col sm={8} className="manifest-actions">
232
+ <Spinner loading={actionInProgress} inline />
233
+ {simpleContentAccess ? __('Yes') : __('No')}
234
+ <OverlayTrigger
235
+ overlay={
236
+ <Tooltip id="sca-tooltip">
237
+ {__('When Simple Content Access is enabled, hosts can consume from all repositories in their Content View regardless of subscription status.')}
238
+ </Tooltip>
239
+ }
240
+ placement="bottom"
241
+ trigger={['hover', 'focus']}
242
+ rootClose={false}
243
+ >
244
+ <Icon type="pf" name="info" />
245
+ </OverlayTrigger>
237
246
  {canImportManifest &&
238
- <TooltipButton
239
- onClick={this.refreshManifest}
240
- tooltipId="refresh-manifest-button-tooltip"
241
- tooltipText={disabledReason}
242
- tooltipPlacement="top"
243
- title={__('Refresh')}
244
- disabled={!manifestExists(organization) ||
245
- actionInProgress || disableManifestActions}
246
- />
247
- }
248
- {canDeleteManifest &&
249
- <React.Fragment>
250
- <TooltipButton
251
- renderedButton={(
252
- <Button
253
- disabled={!manifestExists(organization) || actionInProgress}
254
- bsStyle="danger"
255
- onClick={() => this.showDeleteManifestModal(true)}
256
- >
257
- {__('Delete')}
258
- </Button>
259
- )}
260
-
261
- tooltipId="delete-manifest-button-tooltip"
262
- tooltipText={this.disabledTooltipText()}
263
- tooltipPlacement="top"
264
-
265
- />
266
- </React.Fragment>
267
- }
268
- </div>
269
-
270
- <MessageDialog
271
- show={this.state.showDeleteManifestModalDialog}
272
- title={__('Confirm delete manifest')}
273
- secondaryContent={
274
- // eslint-disable-next-line react/no-danger
275
- <p dangerouslySetInnerHTML={{
276
- __html: DeleteManifestModalText,
277
- }}
247
+ <FormControl
248
+ id="usmaFile"
249
+ type="file"
250
+ accept=".zip"
251
+ disabled={actionInProgress}
252
+ onChange={e => this.uploadManifest(e.target.files)}
278
253
  />
279
254
  }
280
- primaryActionButtonContent={__('Delete')}
281
- primaryActionButtonBsStyle="danger"
282
- primaryAction={() => this.deleteManifest()}
283
- secondaryActionButtonContent={__('Cancel')}
284
- secondaryAction={() => this.showDeleteManifestModal(false)}
285
- onHide={() => this.showDeleteManifestModal(false)}
286
- accessibleName="deleteConfirmationDialog"
287
- accessibleDescription="deleteConfirmationDialogContent"
288
- />
289
- </Col>
290
- </FormGroup>
291
- </React.Fragment>
292
- }
293
- </Form>
294
- </Tab>
295
- }
296
- <Tab eventKey={2} title={__('Manifest History')}>
297
- <LoadingState loading={manifestHistory.loading} loadingText={__('Loading')}>
298
- <Table
299
- rows={manifestHistory.results}
300
- columns={columns}
301
- emptyState={emptyStateData()}
302
- />
303
- </LoadingState>
255
+ <div id="manifest-actions-row">
256
+ {canImportManifest &&
257
+ <TooltipButton
258
+ onClick={this.refreshManifest}
259
+ tooltipId="refresh-manifest-button-tooltip"
260
+ tooltipText={disabledReason}
261
+ tooltipPlacement="top"
262
+ title={__('Refresh')}
263
+ disabled={!manifestExists(organization) ||
264
+ actionInProgress || disableManifestActions}
265
+ />
266
+ }
267
+ {canDeleteManifest &&
268
+ <React.Fragment>
269
+ <TooltipButton
270
+ renderedButton={(
271
+ <Button
272
+ disabled={!manifestExists(organization) || actionInProgress}
273
+ bsStyle="danger"
274
+ onClick={this.showDeleteManifestModal}
275
+ >
276
+ {__('Delete')}
277
+ </Button>
278
+ )}
279
+
280
+ tooltipId="delete-manifest-button-tooltip"
281
+ tooltipText={this.disabledTooltipText()}
282
+ tooltipPlacement="top"
283
+
284
+ />
285
+ </React.Fragment>
286
+ }
287
+ </div>
288
+ <ForemanModal title={__('Confirm delete manifest')} id={DELETE_MANIFEST_MODAL_ID}>
289
+ <DeleteManifestModalText />
290
+ <ForemanModal.Footer>
291
+ <Button bsStyle="default" onClick={this.hideDeleteManifestModal}>
292
+ {__('Cancel')}
293
+ </Button>
294
+ <Button bsStyle="danger" onClick={this.deleteManifest}>
295
+ {__('Delete')}
296
+ </Button>
297
+ </ForemanModal.Footer>
298
+ </ForemanModal>
299
+ </Col>
300
+ </Row>
301
+ </Grid>
302
+ </FormGroup>
303
+ </React.Fragment>
304
+ }
305
+ </Form>
304
306
  </Tab>
305
- </Tabs>
306
- </Modal.Body>
307
- <Modal.Footer>
307
+ }
308
+ <Tab eventKey={2} title={__('Manifest History')}>
309
+ <LoadingState loading={manifestHistory.loading} loadingText={__('Loading')}>
310
+ <Table
311
+ rows={manifestHistory.results}
312
+ columns={columns}
313
+ emptyState={emptyStateData()}
314
+ />
315
+ </LoadingState>
316
+ </Tab>
317
+ </Tabs>
318
+ <ForemanModal.Footer>
308
319
  <Button bsStyle="primary" onClick={this.hideModal}>
309
320
  {__('Close')}
310
321
  </Button>
311
- </Modal.Footer>
312
- </Modal>
322
+ </ForemanModal.Footer>
323
+ </ForemanModal>
313
324
  );
314
325
  }
315
326
  }
@@ -338,12 +349,14 @@ ManageManifestModal.propTypes = {
338
349
  loadOrganization: PropTypes.func.isRequired,
339
350
  saveOrganization: PropTypes.func.isRequired,
340
351
  taskInProgress: PropTypes.bool.isRequired,
352
+ simpleContentAccess: PropTypes.bool,
341
353
  manifestHistory: PropTypes.shape({
342
354
  loading: PropTypes.bool,
343
355
  results: PropTypes.array,
344
356
  }).isRequired,
345
- showModal: PropTypes.bool.isRequired,
346
- onClose: PropTypes.func,
357
+ setModalClosed: PropTypes.func.isRequired,
358
+ setModalOpen: PropTypes.func.isRequired,
359
+ deleteManifestModalIsOpen: PropTypes.bool,
347
360
  };
348
361
 
349
362
  ManageManifestModal.defaultProps = {
@@ -352,7 +365,8 @@ ManageManifestModal.defaultProps = {
352
365
  canImportManifest: false,
353
366
  canDeleteManifest: false,
354
367
  canEditOrganizations: false,
355
- onClose() {},
368
+ deleteManifestModalIsOpen: false,
369
+ simpleContentAccess: false,
356
370
  };
357
371
 
358
372
  export default ManageManifestModal;