katello 3.18.0.rc2 → 3.18.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/katello/katello.scss +0 -72
  3. data/app/controllers/katello/api/v2/api_controller.rb +1 -2
  4. data/app/controllers/katello/api/v2/capsule_content_controller.rb +2 -2
  5. data/app/controllers/katello/api/v2/content_export_incrementals_controller.rb +98 -0
  6. data/app/controllers/katello/api/v2/content_exports_controller.rb +84 -0
  7. data/app/controllers/katello/api/v2/content_imports_controller.rb +59 -0
  8. data/app/controllers/katello/api/v2/content_view_filters_controller.rb +1 -1
  9. data/app/controllers/katello/api/v2/content_view_versions_controller.rb +56 -94
  10. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +2 -1
  11. data/app/controllers/katello/api/v2/repositories_controller.rb +2 -0
  12. data/app/controllers/katello/concerns/api/v2/authorization.rb +14 -1
  13. data/app/lib/actions/katello/applicability/hosts/bulk_generate.rb +6 -2
  14. data/app/lib/actions/katello/capsule_content/sync.rb +1 -1
  15. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +7 -2
  16. data/app/lib/actions/katello/content_view/promote_to_environment.rb +1 -1
  17. data/app/lib/actions/katello/content_view/publish.rb +1 -1
  18. data/app/lib/actions/katello/content_view_version/import.rb +2 -1
  19. data/app/lib/actions/katello/content_view_version/import_library.rb +17 -0
  20. data/app/lib/actions/katello/content_view_version/incremental_update.rb +19 -3
  21. data/app/lib/actions/katello/host/update_system_purpose.rb +1 -1
  22. data/app/lib/actions/katello/host/upload_package_profile.rb +1 -1
  23. data/app/lib/actions/katello/host/upload_profiles.rb +1 -1
  24. data/app/lib/actions/middleware/record_smart_proxy_sync_history.rb +24 -4
  25. data/app/lib/actions/pulp3/content_migration.rb +10 -0
  26. data/app/lib/actions/pulp3/content_migration_presenter.rb +59 -0
  27. data/app/lib/actions/pulp3/content_migration_reset.rb +22 -0
  28. data/app/lib/actions/pulp3/content_view/delete_repository_references.rb +1 -1
  29. data/app/lib/actions/pulp3/content_view_version/export.rb +3 -2
  30. data/app/lib/actions/pulp3/import_migration.rb +6 -1
  31. data/app/lib/actions/pulp3/orchestration/content_view_version/copy_version_units_to_library.rb +2 -1
  32. data/app/lib/actions/pulp3/orchestration/content_view_version/export.rb +17 -13
  33. data/app/lib/actions/pulp3/orchestration/content_view_version/export_library.rb +60 -0
  34. data/app/lib/actions/pulp3/orchestration/content_view_version/import.rb +0 -4
  35. data/app/lib/actions/pulp3/orchestration/repository/import_upload.rb +16 -3
  36. data/app/lib/actions/pulp3/repository/copy_content.rb +1 -1
  37. data/app/lib/actions/pulp3/repository/delete.rb +1 -1
  38. data/app/lib/actions/pulp3/repository/save_version.rb +1 -1
  39. data/app/lib/actions/pulp3/repository/upload_tag.rb +18 -0
  40. data/app/models/katello/authorization/content_view_version.rb +25 -2
  41. data/app/models/katello/authorization/content_view_version_export_history.rb +1 -1
  42. data/app/models/katello/authorization/organization.rb +8 -0
  43. data/app/models/katello/concerns/operatingsystem_extensions.rb +2 -0
  44. data/app/models/katello/concerns/pulp_database_unit.rb +19 -0
  45. data/app/models/katello/concerns/redhat_extensions.rb +2 -2
  46. data/app/models/katello/concerns/smart_proxy_extensions.rb +7 -5
  47. data/app/models/katello/content_migration_progress.rb +4 -0
  48. data/app/models/katello/content_view.rb +5 -0
  49. data/app/models/katello/content_view_history.rb +2 -1
  50. data/app/models/katello/content_view_package_filter.rb +1 -1
  51. data/app/models/katello/content_view_version_export_history.rb +6 -1
  52. data/app/models/katello/file_unit.rb +4 -0
  53. data/app/models/katello/host/subscription_facet.rb +4 -0
  54. data/app/models/katello/repository.rb +7 -0
  55. data/app/models/katello/subscription_status.rb +3 -2
  56. data/app/services/katello/applicability/applicable_content_helper.rb +44 -15
  57. data/app/services/katello/pulp3/api/docker.rb +4 -0
  58. data/app/services/katello/pulp3/content_view_version/export.rb +63 -5
  59. data/app/services/katello/pulp3/content_view_version/import.rb +40 -0
  60. data/app/services/katello/pulp3/content_view_version/import_export_common.rb +0 -16
  61. data/app/services/katello/pulp3/content_view_version/import_validator.rb +26 -49
  62. data/app/services/katello/pulp3/docker_manifest.rb +1 -0
  63. data/app/services/katello/pulp3/docker_tag.rb +1 -0
  64. data/app/services/katello/pulp3/erratum.rb +2 -1
  65. data/app/services/katello/pulp3/migration.rb +95 -12
  66. data/app/services/katello/pulp3/migration_plan.rb +2 -2
  67. data/app/services/katello/pulp3/migration_switchover.rb +21 -5
  68. data/app/services/katello/pulp3/repository.rb +10 -5
  69. data/app/services/katello/pulp3/repository/docker.rb +5 -0
  70. data/app/services/katello/pulp3/repository/yum.rb +23 -8
  71. data/app/services/katello/pulp3/task.rb +4 -0
  72. data/app/services/katello/pulp3/task_group.rb +4 -0
  73. data/app/views/katello/api/v2/content_views/show.json.rabl +6 -0
  74. data/app/views/katello/layouts/react.html.erb +3 -2
  75. data/app/views/katello/sync_management/_products.html.erb +1 -1
  76. data/app/views/overrides/activation_keys/_host_tab_pane.html.erb +1 -5
  77. data/config/routes/api/v2.rb +23 -3
  78. data/db/migrate/20150930183738_migrate_content_hosts.rb +1 -1
  79. data/db/migrate/20200514092553_move_katello_fields_from_hostgroups.katello.rb +5 -2
  80. data/db/migrate/20201119211133_pulp3_migration_progress.rb +9 -0
  81. data/db/migrate/20210201165835_add_migration_missing_content.rb +12 -0
  82. data/engines/bastion/app/assets/javascripts/bastion/auth/authorization.service.js +1 -1
  83. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/common/views/katello-agent-notice.html +1 -1
  84. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-system-purpose-modal.html +35 -40
  85. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-client.html +1 -1
  86. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/deletion/content-view-version-deletion-activation-keys.controller.js +8 -3
  87. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/deletion/content-view-version-deletion-content-hosts.controller.js +9 -3
  88. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-details.html +1 -1
  89. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-publish.html +4 -0
  90. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +78 -7
  91. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de.po +17 -20
  92. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/es.po +17 -24
  93. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +1292 -1170
  94. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/it.po +17 -20
  95. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +858 -807
  96. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +18 -19
  97. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt_BR.po +17 -24
  98. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ru.po +17 -18
  99. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +986 -971
  100. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_TW.po +19 -20
  101. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/translations.js +9 -9
  102. data/lib/katello/permission_creator.rb +23 -3
  103. data/lib/katello/tasks/delete_orphaned_content.rake +1 -3
  104. data/lib/katello/tasks/pulp3_content_switchover.rake +3 -1
  105. data/lib/katello/tasks/pulp3_migration.rake +25 -6
  106. data/lib/katello/tasks/pulp3_migration_abort.rake +7 -2
  107. data/lib/katello/tasks/pulp3_migration_approve_corrupted.rake +16 -0
  108. data/lib/katello/tasks/pulp3_migration_reset.rake +26 -0
  109. data/lib/katello/tasks/pulp3_migration_stats.rake +61 -8
  110. data/lib/katello/tasks/pulp3_post_migration_check.rake +1 -3
  111. data/lib/katello/tasks/receptor/extract_orgs.rake +1 -1
  112. data/lib/katello/tasks/reports.rake +4 -1
  113. data/lib/katello/tasks/repository.rake +3 -5
  114. data/lib/katello/version.rb +1 -1
  115. data/locale/action_names.rb +51 -51
  116. data/locale/bn/katello.po +136 -51
  117. data/locale/cs/katello.po +136 -49
  118. data/locale/de/katello.po +136 -48
  119. data/locale/en/katello.po +136 -48
  120. data/locale/es/katello.po +136 -48
  121. data/locale/fr/katello.po +136 -48
  122. data/locale/gu/katello.po +136 -51
  123. data/locale/hi/katello.po +136 -51
  124. data/locale/it/katello.po +136 -48
  125. data/locale/ja/katello.po +136 -48
  126. data/locale/katello.pot +941 -767
  127. data/locale/kn/katello.po +136 -51
  128. data/locale/ko/katello.po +136 -48
  129. data/locale/mr/katello.po +136 -51
  130. data/locale/or/katello.po +136 -51
  131. data/locale/pa/katello.po +136 -51
  132. data/locale/pt/katello.po +136 -51
  133. data/locale/pt_BR/katello.po +136 -48
  134. data/locale/ru/katello.po +136 -48
  135. data/locale/ta/katello.po +136 -51
  136. data/locale/te/katello.po +136 -51
  137. data/locale/zh_CN/katello.po +136 -48
  138. data/locale/zh_TW/katello.po +136 -48
  139. data/webpack/components/TypeAhead/TypeAhead.js +2 -1
  140. data/webpack/components/TypeAhead/pf3Search/TypeAheadSearch.js +2 -1
  141. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +7 -2
  142. data/webpack/scenes/Subscriptions/Manifest/index.js +1 -0
  143. metadata +31 -19
  144. data/lib/katello/tasks/common.rake +0 -7
@@ -43,9 +43,10 @@ module Katello
43
43
 
44
44
  def to_status(options = {})
45
45
  return UNKNOWN unless host.subscription_facet.try(:uuid)
46
- status_override = 'unsubscribed_hypervisor' if host.subscription_facet.hypervisor && !host.subscription_facet.candlepin_consumer.entitlements?
46
+ return DISABLED if host.organization.simple_content_access?
47
+ status_override = 'unsubscribed_hypervisor' if host.subscription_facet.unsubscribed_hypervisor?
47
48
  status_override ||= options.fetch(:status_override, nil)
48
- status = status_override || Katello::Candlepin::Consumer.new(host.subscription_facet.uuid, host.organization.label).entitlement_status
49
+ status = status_override || host.subscription_facet.candlepin_consumer.entitlement_status
49
50
 
50
51
  case status
51
52
  when Katello::Candlepin::Consumer::ENTITLEMENTS_DISABLED
@@ -60,17 +60,7 @@ module Katello
60
60
  end
61
61
 
62
62
  def fetch_rpm_content_ids
63
- # Query for applicable RPM ids
64
- # -> Include all non-modular rpms or rpms that exist within installed module streams
65
- enabled_module_stream_ids = ::Katello::ModuleStream.
66
- joins("inner join katello_available_module_streams on
67
- katello_module_streams.name = katello_available_module_streams.name and
68
- katello_module_streams.stream = katello_available_module_streams.stream").
69
- joins("inner join katello_host_available_module_streams on
70
- katello_available_module_streams.id = katello_host_available_module_streams.available_module_stream_id").
71
- where("katello_host_available_module_streams.host_id = :content_facet_id and
72
- katello_host_available_module_streams.status = 'enabled'",
73
- :content_facet_id => self.content_facet.host.id).select(:id)
63
+ enabled_module_stream_ids = fetch_enabled_module_stream_ids
74
64
 
75
65
  ::Katello::Rpm.
76
66
  joins("INNER JOIN katello_repository_rpms ON
@@ -78,7 +68,8 @@ module Katello
78
68
  joins("INNER JOIN katello_installed_packages ON
79
69
  katello_rpms.name = katello_installed_packages.name AND
80
70
  katello_rpms.arch = katello_installed_packages.arch AND
81
- katello_rpms.evr > katello_installed_packages.evr").
71
+ katello_rpms.evr > katello_installed_packages.evr AND
72
+ katello_installed_packages.id in (#{newest_distinct_installed_packages_query})").
82
73
  joins("LEFT JOIN katello_module_stream_rpms ON
83
74
  katello_rpms.id = katello_module_stream_rpms.rpm_id").
84
75
  joins("INNER JOIN katello_host_installed_packages ON
@@ -87,9 +78,47 @@ module Katello
87
78
  :bound_library_repos => self.bound_library_instance_repos).
88
79
  where("katello_host_installed_packages.host_id = :content_facet_id",
89
80
  :content_facet_id => self.content_facet.host.id).
90
- where("katello_module_stream_rpms.module_stream_id is null or
91
- katello_module_stream_rpms.module_stream_id in (:enabled_module_streams)",
92
- :enabled_module_streams => enabled_module_stream_ids).pluck(:id).uniq
81
+ where("(katello_module_stream_rpms.module_stream_id IS NULL AND
82
+ katello_installed_packages.id NOT IN (:locked_modular_installed_packages)) OR
83
+ (katello_module_stream_rpms.module_stream_id IN (:enabled_module_streams)
84
+ AND katello_installed_packages.id IN (:locked_modular_installed_packages))",
85
+ :enabled_module_streams => enabled_module_stream_ids,
86
+ :locked_modular_installed_packages => locked_modular_installed_packages(enabled_module_stream_ids)).pluck(:id).uniq
87
+ end
88
+
89
+ def fetch_enabled_module_stream_ids
90
+ # Query for applicable RPM ids
91
+ # -> Include all non-modular rpms or rpms that exist within installed module streams
92
+ ::Katello::ModuleStream.
93
+ joins("inner join katello_available_module_streams on
94
+ katello_module_streams.name = katello_available_module_streams.name and
95
+ katello_module_streams.stream = katello_available_module_streams.stream").
96
+ joins("inner join katello_host_available_module_streams on
97
+ katello_available_module_streams.id = katello_host_available_module_streams.available_module_stream_id").
98
+ where("katello_host_available_module_streams.host_id = :content_facet_id and
99
+ katello_host_available_module_streams.status = 'enabled'",
100
+ :content_facet_id => self.content_facet.host.id).select(:id)
101
+ end
102
+
103
+ # Installed packages that are locked for the host due to enabled module stream membership
104
+ def locked_modular_installed_packages(enabled_module_streams)
105
+ rpms_in_enabled_module_streams = ::Katello::Rpm.
106
+ joins("INNER JOIN katello_module_stream_rpms ON katello_rpms.id = katello_module_stream_rpms.rpm_id").
107
+ where("katello_module_stream_rpms.module_stream_id IN (:enabled_module_streams)",
108
+ :enabled_module_streams => enabled_module_streams).select(:nvra, :epoch)
109
+
110
+ ::Katello::InstalledPackage.where(nvra: rpms_in_enabled_module_streams.map(&:nvra),
111
+ epoch: rpms_in_enabled_module_streams.map(&:epoch)).select(:id)
112
+ end
113
+
114
+ def newest_distinct_installed_packages_query
115
+ "SELECT DISTINCT ON (katello_installed_packages.name) katello_installed_packages.id " \
116
+ "FROM katello_installed_packages INNER JOIN " \
117
+ "katello_host_installed_packages ON " \
118
+ "katello_installed_packages.id = " \
119
+ "katello_host_installed_packages.installed_package_id " \
120
+ "WHERE katello_host_installed_packages.host_id = " \
121
+ "#{content_facet.host.id} ORDER BY katello_installed_packages.name, katello_installed_packages.evr DESC"
93
122
  end
94
123
 
95
124
  def applicable_differences
@@ -32,6 +32,10 @@ module Katello
32
32
  PulpContainerClient::RecursiveManage
33
33
  end
34
34
 
35
+ def self.tag_image_class
36
+ PulpContainerClient::TagImage
37
+ end
38
+
35
39
  def api_client
36
40
  PulpContainerClient::ApiClient.new(smart_proxy.pulp3_configuration(PulpContainerClient::Configuration))
37
41
  end
@@ -4,13 +4,37 @@ module Katello
4
4
  class Export
5
5
  include ImportExportCommon
6
6
 
7
- def initialize(smart_proxy:, content_view_version: nil, destination_server: nil, from_content_view_version: nil)
7
+ def initialize(smart_proxy:,
8
+ content_view_version: nil,
9
+ destination_server: nil,
10
+ from_content_view_version: nil)
8
11
  @smart_proxy = smart_proxy
9
12
  @content_view_version = content_view_version
10
13
  @destination_server = destination_server
11
14
  @from_content_view_version = from_content_view_version
12
15
  end
13
16
 
17
+ def repository_hrefs
18
+ version_hrefs.map { |href| version_href_to_repository_href(href) }.uniq
19
+ end
20
+
21
+ def version_hrefs
22
+ repositories.pluck(:version_href).compact
23
+ end
24
+
25
+ def repositories(fetch_all: false)
26
+ repos = if @content_view_version.default?
27
+ @content_view_version.repositories.yum_type
28
+ else
29
+ @content_view_version.archived_repos.yum_type
30
+ end
31
+ if fetch_all
32
+ repos
33
+ else
34
+ repos.immediate
35
+ end
36
+ end
37
+
14
38
  def generate_exporter_path
15
39
  export_path = "#{@content_view_version.content_view}/#{@content_view_version.version}/#{@destination_server}/#{date_dir}".gsub(/\s/, '_')
16
40
  "#{@content_view_version.organization.label}/#{export_path}"
@@ -66,12 +90,29 @@ module Katello
66
90
  api.exporter_api.delete(exporter_href)
67
91
  end
68
92
 
93
+ def validate!(fail_on_missing_content: true, validate_incremental: true)
94
+ validate_repositories_immediate! if fail_on_missing_content
95
+ validate_incremental_export! if validate_incremental && !@from_content_view_version.blank?
96
+ end
97
+
98
+ def validate_repositories_immediate!
99
+ non_immediate_repos = repositories(fetch_all: true).non_immediate
100
+ if non_immediate_repos.any?
101
+ fail _("NOTE: Unable to fully export Content View Version '%{content_view} %{current}'"\
102
+ " it contains repositories without the 'immediate' download policy."\
103
+ " Update the download policy and sync affected repositories. Once synced republish the content view"\
104
+ " and export the generated version. \n %{repos}" %
105
+ { content_view: @content_view_version.content_view.name,
106
+ current: @content_view_version.version,
107
+ repos: self.class.generate_product_repo_strings(repositories: non_immediate_repos)})
108
+ end
109
+ end
110
+
69
111
  def validate_incremental_export!
70
- return if @from_content_view_version.blank?
71
112
  from_exporter = Export.new(smart_proxy: @smart_proxy, content_view_version: @from_content_view_version)
72
113
 
73
- from_exporter_repos = generate_repo_mapping(from_exporter.repositories)
74
- to_exporter_repos = generate_repo_mapping(repositories)
114
+ from_exporter_repos = generate_repo_mapping(from_exporter.repositories(fetch_all: true))
115
+ to_exporter_repos = generate_repo_mapping(repositories(fetch_all: true))
75
116
 
76
117
  invalid_repos_exist = (from_exporter_repos.keys & to_exporter_repos.keys).any? do |repo_id|
77
118
  from_exporter_repos[repo_id] != to_exporter_repos[repo_id]
@@ -99,7 +140,8 @@ module Katello
99
140
  ret = { organization: @content_view_version.organization.name,
100
141
  repository_mapping: {},
101
142
  content_view: @content_view_version.content_view.name,
102
- content_view_version: @content_view_version.slice(:major, :minor)
143
+ content_view_version: @content_view_version.slice(:major, :minor),
144
+ incremental: @from_content_view_version.present?
103
145
  }
104
146
 
105
147
  unless @from_content_view_version.blank?
@@ -120,6 +162,22 @@ module Katello
120
162
  end
121
163
  ret
122
164
  end
165
+
166
+ def self.find_library_export_view(create_by_default: false,
167
+ destination_server:,
168
+ organization:)
169
+ name = "Export-Library"
170
+ name += "-#{destination_server}" unless destination_server.blank?
171
+ select_method = create_by_default ? :first_or_create : :first
172
+ ::Katello::ContentView.where(name: name, organization: organization).send(select_method)
173
+ end
174
+
175
+ def self.generate_product_repo_strings(repositories:)
176
+ repositories.map do |repo|
177
+ _("Product: '%{product}', Repository: '%{repository}'" % { product: repo.product.name,
178
+ repository: repo.name})
179
+ end
180
+ end
123
181
  end
124
182
  end
125
183
  end
@@ -46,6 +46,46 @@ module Katello
46
46
  def self.check!(content_view:, metadata:, path:)
47
47
  ImportValidator.new(content_view: content_view, metadata: metadata, path: path).check!
48
48
  end
49
+
50
+ def self.reset_content_view_repositories_from_metadata!(content_view:, metadata:)
51
+ # Given metadata from the dump and a content view
52
+ # this method
53
+ # 1) Fetches ids of the library repos whose product name, repo name amd redhat?
54
+ # => match values provided in the metadata's repository mapping
55
+ # 2) Removes all the repositories associated to this content view
56
+ # 3) Adds the repositories matched from the dump
57
+ # The main intent of this method is to assume that the user intends for the
58
+ # content view to exaclty look like what is specified in metadata
59
+
60
+ repos_in_library = Katello::Repository.
61
+ in_default_view.
62
+ yum_type.
63
+ joins(:product => :provider, :content_view_version => :content_view).
64
+ joins(:root).
65
+ where("#{::Katello::ContentView.table_name}.organization_id" => content_view.organization_id).
66
+ pluck("#{::Katello::Repository.table_name}.id",
67
+ "#{::Katello::RootRepository.table_name}.name",
68
+ "#{::Katello::Product.table_name}.name",
69
+ "#{::Katello::Provider.table_name}.provider_type"
70
+ )
71
+ repos_in_library_map = {}
72
+ # repos_in_library_map is going to look like {['repo1', 'product1', false] => 100, ['repo1', 'product1', true] => 200 }
73
+ repos_in_library.each do |id, repo, product, provider_type|
74
+ repos_in_library_map[[repo, product, provider_type == Katello::Provider::REDHAT]] = id
75
+ end
76
+
77
+ repo_ids = metadata[:repository_mapping].values.map do |repo|
78
+ repos_in_library_map[[repo[:repository], repo[:product], repo[:redhat]]]
79
+ end
80
+ content_view.update!(repository_ids: repo_ids)
81
+ end
82
+
83
+ def self.find_or_create_library_import_view(organization)
84
+ name = ::Katello::ContentView::IMPORT_LIBRARY
85
+ ::Katello::ContentView.where(name: name,
86
+ organization: organization,
87
+ import_only: true).first_or_create
88
+ end
49
89
  end
50
90
  end
51
91
  end
@@ -19,22 +19,6 @@ module Katello
19
19
  repo_api.read(version_href_to_repository_href(version_href))
20
20
  end
21
21
 
22
- def repository_hrefs
23
- version_hrefs.map { |href| version_href_to_repository_href(href) }.uniq
24
- end
25
-
26
- def version_hrefs
27
- repositories.pluck(:version_href).compact
28
- end
29
-
30
- def repositories
31
- if @content_view_version.default?
32
- @content_view_version.repositories.yum_type
33
- else
34
- @content_view_version.archived_repos.yum_type
35
- end
36
- end
37
-
38
22
  def version_href_to_repository_href(version_href)
39
23
  version_href.split("/")[0..-3].join("/") + "/"
40
24
  end
@@ -2,7 +2,6 @@ module Katello
2
2
  module Pulp3
3
3
  module ContentViewVersion
4
4
  class ImportValidator
5
- BASEDIR = '/var/lib/pulp'.freeze
6
5
  attr_accessor :metadata, :path, :content_view
7
6
  def initialize(content_view:, path:, metadata:)
8
7
  self.content_view = content_view
@@ -11,10 +10,11 @@ module Katello
11
10
  end
12
11
 
13
12
  def check!
14
- check_permissions!
15
- ensure_importing_cvv_does_not_exist!
16
- ensure_from_cvv_exists!
17
- ensure_repositories_metadata_and_content_view_match!
13
+ unless content_view.default?
14
+ ensure_importing_cvv_does_not_exist!
15
+ ensure_from_cvv_exists!
16
+ end
17
+ ensure_repositories_metadata_are_in_the_library!
18
18
  end
19
19
 
20
20
  def ensure_importing_cvv_does_not_exist!
@@ -43,55 +43,32 @@ module Katello
43
43
  end
44
44
  end
45
45
 
46
- def ensure_repositories_metadata_and_content_view_match!
47
- product_repos_in_content_view = content_view.repositories.yum_type.map { |repo| [repo.product.name, repo.name, repo.redhat?] }
46
+ def ensure_repositories_metadata_are_in_the_library!
47
+ repos_in_library = Katello::Repository.
48
+ in_default_view.
49
+ yum_type.
50
+ joins(:product => :provider, :content_view_version => :content_view).
51
+ joins(:root).
52
+ where("#{::Katello::ContentView.table_name}.organization_id" => content_view.organization_id).
53
+ pluck("#{::Katello::Product.table_name}.name",
54
+ "#{::Katello::RootRepository.table_name}.name",
55
+ "#{::Katello::Provider.table_name}.provider_type"
56
+ )
57
+
58
+ # repos_in_library look like [["prod1", "repo1", "Anonymous"], ["prod2", "repo2", "Red Hat"]]
59
+ product_repos_in_library = repos_in_library.map { |product, repo, provider| [product, repo, provider == ::Katello::Provider::REDHAT] }
48
60
  product_repos_in_metadata = metadata[:repository_mapping].values.map { |repo| [repo[:product], repo[:repository], repo[:redhat]] }
49
-
50
- product_repos_in_content_view.sort!
51
- product_repos_in_metadata.sort!
52
- # product_repos_in_content_view & product_repos_in_metadata look like [["prod1", "repo1", false], ["prod2", "repo2", false]]
53
-
54
- if product_repos_in_content_view != product_repos_in_metadata
55
- repos_in_content_view = generate_product_repo_i18n_string(product_repos_in_content_view)
56
- repos_in_import = generate_product_repo_i18n_string(product_repos_in_metadata)
57
-
58
- fail _("Repositories in the importing content view do not match the repositories provided in the import metadata.\n "\
59
- "Repositories in Content View '%{content_view}': %{repos_in_content_view}\n "\
60
- "Repositories in the Import Metadata: %{repos_in_import}" % { content_view: content_view.name,
61
- repos_in_content_view: repos_in_content_view.join(""),
62
- repos_in_import: repos_in_import.join("")}
61
+ # product_repos_in_library & product_repos_in_metadata look like [["prod1", "repo1", false], ["prod2", "repo2", false]]
62
+ product_repos_not_in_library = product_repos_in_metadata - product_repos_in_library
63
+ unless product_repos_not_in_library.blank?
64
+ repos_in_import = generate_product_repo_i18n_string(product_repos_not_in_library)
65
+ fail _("The following repositories provided in the import metadata are either not available in the Library or are of incorrect Respository Type. "\
66
+ "Please add or enable the repositories before importing\n "\
67
+ "%{repos}" % { content_view: content_view.name, repos: repos_in_import.join("")}
63
68
  )
64
69
  end
65
70
  end
66
71
 
67
- def check_permissions!
68
- fail _("Invalid path specified.") if path.blank? || !File.directory?(path)
69
- fail _("The import path must be in a subdirectory under '%s'." % BASEDIR) unless path.starts_with?(BASEDIR)
70
- fail _("Pulp user or group unable to read content in '%s'." % path) unless pulp_user_accessible?(path)
71
-
72
- Dir.glob("#{path}/*").each do |file|
73
- fail _("Pulp user or group unable to read '%s'." % file) unless pulp_user_accessible?(file)
74
- end
75
- toc_path = "#{path}/#{metadata[:toc]}"
76
- fail _("The TOC file specified in the metadata does not exist. %s " % toc_path) unless File.exist?(toc_path)
77
- end
78
-
79
- def pulp_user_accessible?(path)
80
- pulp_info = fetch_pulp_user_info
81
- return false if pulp_info.blank?
82
-
83
- stat = File.stat(path)
84
- stat.gid.to_s == pulp_info.gid ||
85
- stat.uid.to_s == pulp_info.uid ||
86
- stat.mode.to_s(8)[-1].to_i >= 4
87
- end
88
-
89
- def fetch_pulp_user_info
90
- pulp_user = nil
91
- Etc.passwd { |u| pulp_user = u if u.name == 'pulp' }
92
- pulp_user
93
- end
94
-
95
72
  def generate_product_repo_i18n_string(product_repos)
96
73
  # product_repos look like [["prod1", "repo1", false], ["prod2", "repo2", false]]
97
74
  product_repos.map do |product, repo, redhat|
@@ -2,6 +2,7 @@ module Katello
2
2
  module Pulp3
3
3
  class DockerManifest < PulpContentUnit
4
4
  include LazyAccessor
5
+ CONTENT_TYPE = "docker_manifest".freeze
5
6
 
6
7
  def self.content_api
7
8
  PulpContainerClient::ContentManifestsApi.new(Katello::Pulp3::Api::Docker.new(SmartProxy.pulp_primary!).api_client)
@@ -2,6 +2,7 @@ module Katello
2
2
  module Pulp3
3
3
  class DockerTag < PulpContentUnit
4
4
  include LazyAccessor
5
+ CONTENT_TYPE = "docker_tag".freeze
5
6
 
6
7
  def self.content_api
7
8
  PulpContainerClient::ContentTagsApi.new(Katello::Pulp3::Api::Docker.new(SmartProxy.pulp_primary!).api_client)
@@ -35,7 +35,8 @@ module Katello
35
35
  custom_json["issued"] = convert_date_if_epoch(custom_json["issued"])
36
36
  custom_json["updated"] = convert_date_if_epoch(custom_json["updated"]) unless custom_json["updated"].blank?
37
37
 
38
- if model.updated.blank? || (custom_json['updated'].to_datetime != model.updated.to_datetime)
38
+ if model.updated.blank? ||
39
+ (custom_json['updated'] && (custom_json['updated'].to_datetime != model.updated.to_datetime))
39
40
  custom_json['errata_id'] = custom_json.delete('id')
40
41
  custom_json['errata_type'] = custom_json.delete('type')
41
42
  custom_json['updated'] = custom_json['updated'].blank? ? custom_json['issued'] : custom_json['updated']
@@ -3,7 +3,7 @@ require 'pulp_2to3_migration_client'
3
3
  module Katello
4
4
  module Pulp3
5
5
  class Migration
6
- attr_accessor :smart_proxy, :reimport_all
6
+ attr_accessor :smart_proxy, :reimport_all, :task_id
7
7
  GET_QUERY_ID_LENGTH = 90
8
8
 
9
9
  MUTABLE_CONTENT_TYPES = [
@@ -15,14 +15,20 @@ module Katello
15
15
  Katello::Erratum
16
16
  ].freeze
17
17
 
18
+ CORRUPTABLE_CONTENT_TYPES = [
19
+ Katello::Rpm, Katello::FileUnit
20
+ ].freeze
21
+
18
22
  def self.repository_types_for_migration
19
23
  #we can migrate types that pulp3 supports, but are overridden to pulp2. These are in 'migration mode'
20
24
  overridden = (SETTINGS[:katello][:use_pulp_2_for_content_type] || {}).keys.select { |key| SETTINGS[:katello][:use_pulp_2_for_content_type][key] }
21
25
  overridden.select { |type| SmartProxy.pulp_primary.pulp3_repository_type_support?(type.to_s, false) }.map { |t| t.to_s }
22
26
  end
23
27
 
24
- def initialize(smart_proxy, repository_types = Migration.repository_types_for_migration, options = {})
25
- self.reimport_all = options.fetch(:reimport, false)
28
+ def initialize(smart_proxy, options = {})
29
+ self.task_id = options.fetch(:task_id, nil)
30
+ self.reimport_all = options.fetch(:reimport_all, false)
31
+ repository_types = options.fetch(:repository_types, Migration.repository_types_for_migration)
26
32
 
27
33
  if (repository_types - smart_proxy.supported_pulp_types[:pulp3][:overriden_to_pulp2]).any?
28
34
  fail ::Katello::Errors::Pulp3MigrationError, _("Pulp 3 migration cannot run. Types %s have already been migrated.") %
@@ -62,6 +68,15 @@ module Katello
62
68
  [YumMetadataFile]
63
69
  end
64
70
 
71
+ def last_successful_migration_time
72
+ task = ForemanTasks::Task.where(:label => Actions::Pulp3::ContentMigration.to_s, :result => 'success').order("started_at desc").first
73
+ if reimport_all || task.nil?
74
+ 0
75
+ else
76
+ task.started_at.to_i
77
+ end
78
+ end
79
+
65
80
  def content_types_for_migration
66
81
  content_types = @repository_types.collect do |repository_type_label|
67
82
  Katello::RepositoryTypeManager.repository_types[repository_type_label].content_types_to_index
@@ -70,7 +85,20 @@ module Katello
70
85
  content_types.flatten - Migration.ignorable_content_types
71
86
  end
72
87
 
88
+ def update_import_status(message, index = nil)
89
+ #reduce output updating, only update every 20 items
90
+ if (index.nil? || index % 20 == 0) && self.task_id
91
+ progress = Katello::ContentMigrationProgress.find_or_create_by(:task_id => self.task_id)
92
+ progress.update(:progress_message => message)
93
+ progress.save!
94
+
95
+ fail Katello::Errors::Pulp3MigrationError, "Cancelled by user." if progress.canceled?
96
+ end
97
+ end
98
+
73
99
  def import_pulp3_content
100
+ update_import_status("Starting katello import phase.")
101
+
74
102
  Katello::Logging.time("CONTENT_MIGRATION - Total Import Process") do
75
103
  @repository_types.each do |repository_type_label|
76
104
  Katello::Logging.time("CONTENT_MIGRATION - Importing Repository", data: {type: repository_type_label}) do
@@ -78,8 +106,9 @@ module Katello
78
106
  end
79
107
 
80
108
  Katello::RepositoryTypeManager.repository_types[repository_type_label].content_types_to_index.each do |content_type|
81
- Katello::Logging.time("CONTENT_MIGRATION - Importing Content", data: {type: content_type}) do
109
+ Katello::Logging.time("CONTENT_MIGRATION - Importing Content", data: {type: content_type.label}) do
82
110
  import_content_type(content_type)
111
+ mark_missing_content(content_type)
83
112
  end
84
113
  end
85
114
  end
@@ -90,6 +119,44 @@ module Katello
90
119
  Katello::Pulp3::MigrationPlan.new(@repository_types).generate.as_json
91
120
  end
92
121
 
122
+ def reset
123
+ if @repository_types.empty?
124
+ fail ::Katello::Errors::Pulp3MigrationError, 'There are no Pulp 3 content types to reset'
125
+ end
126
+
127
+ plugins = @repository_types.sort.map do |repository_type|
128
+ {
129
+ type: ::Katello::Pulp3::MigrationPlan.pulp2_repository_type(repository_type)
130
+ }
131
+ end
132
+ plan = { plugins: plugins }
133
+
134
+ # TODO: Don't provide the plan as a string once this is resolved: https://pulp.plan.io/issues/8211
135
+ migration_plan_api.reset(migration_plan_api.create(plan: plan).pulp_href, plan.to_json)
136
+
137
+ content_types_for_migration.each do |content_type|
138
+ if content_type.model_class == ::Katello::Erratum
139
+ ::Katello::RepositoryErratum.update_all(erratum_pulp3_href: nil)
140
+ else
141
+ content_type.model_class.update_all(migrated_pulp3_href: nil)
142
+ end
143
+ end
144
+
145
+ @repository_types.each do |repo_type|
146
+ if repo_type == "file"
147
+ ::Katello::Repository.file_type.update(remote_href: nil, publication_href: nil, version_href: nil)
148
+ elsif repo_type == "docker"
149
+ ::Katello::Repository.docker_type.update(remote_href: nil, publication_href: nil, version_href: nil)
150
+ elsif repo_type == "yum"
151
+ ::Katello::Repository.yum_type.update(remote_href: nil, publication_href: nil, version_href: nil)
152
+ end
153
+ end
154
+
155
+ ::Katello::Pulp3::RepositoryReference.destroy_all
156
+ ::Katello::Pulp3::DistributionReference.destroy_all
157
+ ::Katello::Pulp3::ContentGuard.destroy_all
158
+ end
159
+
93
160
  def create_migrations
94
161
  plan = migration_plan
95
162
  Rails.logger.info("Migration Plan: #{plan}")
@@ -103,7 +170,7 @@ module Katello
103
170
  end
104
171
 
105
172
  def start_migration(plan_href)
106
- migration_plan_api.run(plan_href, dry_run: false, validate: true)
173
+ migration_plan_api.run(plan_href, dry_run: false, validate: true, skip_corrupted: true)
107
174
  end
108
175
 
109
176
  def import_repositories(repository_type_label)
@@ -114,7 +181,9 @@ module Katello
114
181
  if repository_type_label == 'yum'
115
182
  import_yum_repos(imported, katello_repos)
116
183
  else
117
- katello_repos.each do |repo|
184
+ repo_count = katello_repos.count
185
+ katello_repos.each_with_index do |repo, index|
186
+ update_import_status("Importing migrated content units #{repository_type_label}: #{index + 1}/#{repo_count}", index)
118
187
  found = imported.find { |migrated_repo| migrated_repo.pulp2_repo_id == repo.pulp_id }
119
188
  import_repo(repo, found) if found
120
189
  end
@@ -122,7 +191,9 @@ module Katello
122
191
  end
123
192
 
124
193
  def import_yum_repos(migrated_repo_items, repos)
125
- repos.each do |yum_repo|
194
+ repo_count = repos.count
195
+ repos.each_with_index do |yum_repo, index|
196
+ update_import_status("Importing migrated yum repositories: #{index + 1}/#{repo_count}", index)
126
197
  to_find = nil
127
198
  if yum_repo.content_view.composite?
128
199
  if yum_repo.link?
@@ -195,14 +266,16 @@ module Katello
195
266
  end
196
267
 
197
268
  def operate_on_errata
269
+ last_migration_time = last_successful_migration_time
198
270
  offset = 0
199
- limit = 300
200
- response = pulp2_content_api.list(pulp2_content_type_id: 'erratum', offset: offset, limit: limit)
271
+ limit = SETTINGS[:katello][:pulp][:bulk_load_size]
272
+ response = pulp2_content_api.list(pulp2_content_type_id: 'erratum', offset: offset, limit: limit, pulp2_last_updated__gt: last_migration_time)
201
273
  total_count = response.count
202
274
  yield(response.results)
203
275
  until (offset + limit > total_count)
204
276
  offset += limit
205
- response = pulp2_content_api.list(pulp2_content_type_id: 'erratum', offset: offset, limit: limit)
277
+ response = pulp2_content_api.list(pulp2_content_type_id: 'erratum', offset: offset, limit: limit, pulp2_last_updated__gt: last_migration_time)
278
+ update_import_status("Importing migrated content type erratum: #{offset + limit}/#{total_count}")
206
279
  yield(response.results)
207
280
  end
208
281
  end
@@ -231,6 +304,12 @@ module Katello
231
304
  on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
232
305
  end
233
306
 
307
+ def mark_missing_content(content_type)
308
+ unless [Katello::DockerTag, Katello::DockerManifest, Katello::Erratum].include?(content_type.model_class)
309
+ content_type.model_class.where(:migrated_pulp3_href => nil).update_all(:missing_from_migration => true)
310
+ end
311
+ end
312
+
234
313
  def import_content_type(content_type)
235
314
  if content_type.model_class == Katello::Erratum
236
315
  import_errata
@@ -242,11 +321,15 @@ module Katello
242
321
  unmigrated_units = unmigrated_units.where(:migrated_pulp3_href => nil)
243
322
  end
244
323
 
324
+ total_count = unmigrated_units.count
325
+ current_count = 0
326
+
245
327
  unmigrated_units.select(:id, :pulp_id).find_in_batches(batch_size: GET_QUERY_ID_LENGTH) do |needing_hrefs|
328
+ current_count += needing_hrefs.count
329
+ update_import_status("Importing migrated content type #{content_type.label}: #{current_count}/#{total_count}")
246
330
  migrated_units = pulp2_content_api.list(pulp2_id__in: needing_hrefs.map { |unit| unit.pulp_id }.join(','))
247
331
  migrated_units.results.each do |migrated_unit|
248
- matching_record = needing_hrefs.find { |db_unit| db_unit.pulp_id == migrated_unit.pulp2_id }
249
- matching_record&.update_column(:migrated_pulp3_href, migrated_unit.pulp3_content)
332
+ content_type.model_class.where(pulp_id: migrated_unit.pulp2_id).update_all(migrated_pulp3_href: migrated_unit.pulp3_content)
250
333
  end
251
334
  end
252
335
  end