katello 3.18.1 → 3.18.3.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/katello/katello.scss +0 -72
  3. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +4 -6
  4. data/app/controllers/katello/api/v2/api_controller.rb +1 -2
  5. data/app/controllers/katello/api/v2/content_view_filters_controller.rb +1 -1
  6. data/app/controllers/katello/concerns/api/v2/authorization.rb +14 -1
  7. data/app/lib/actions/katello/host/update_system_purpose.rb +1 -1
  8. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
  9. data/app/lib/actions/katello/repository/sync.rb +5 -1
  10. data/app/lib/actions/pulp3/content_migration_reset.rb +22 -0
  11. data/app/lib/katello/util/hostgroup_facets_helper.rb +126 -0
  12. data/app/lib/katello/util/pulpcore_content_filters.rb +1 -1
  13. data/app/models/katello/concerns/hostgroup_extensions.rb +4 -2
  14. data/app/models/katello/concerns/pulp_database_unit.rb +12 -0
  15. data/app/models/katello/concerns/redhat_extensions.rb +9 -8
  16. data/app/models/katello/concerns/smart_proxy_extensions.rb +3 -1
  17. data/app/models/katello/file_unit.rb +4 -0
  18. data/app/models/katello/host/content_facet.rb +9 -31
  19. data/app/models/katello/ping.rb +35 -15
  20. data/app/services/katello/applicability/applicable_content_helper.rb +40 -20
  21. data/app/services/katello/pulp3/api/core.rb +14 -0
  22. data/app/services/katello/pulp3/erratum.rb +2 -1
  23. data/app/services/katello/pulp3/migration.rb +63 -7
  24. data/app/services/katello/pulp3/migration_plan.rb +2 -2
  25. data/app/services/katello/pulp3/migration_switchover.rb +36 -5
  26. data/app/services/katello/pulp3/repository.rb +40 -10
  27. data/app/services/katello/pulp3/repository/apt.rb +1 -2
  28. data/app/services/katello/pulp3/repository/yum.rb +10 -1
  29. data/app/services/katello/pulp3/rpm.rb +5 -1
  30. data/app/services/katello/pulp3/task.rb +8 -5
  31. data/app/services/katello/pulp3/task_group.rb +13 -5
  32. data/app/views/katello/sync_management/_products.html.erb +1 -1
  33. data/db/migrate/20150930183738_migrate_content_hosts.rb +1 -1
  34. data/db/migrate/20200514092553_move_katello_fields_from_hostgroups.katello.rb +5 -2
  35. data/db/migrate/20210201165835_add_migration_missing_content.rb +12 -0
  36. data/db/migrate/20210420140050_add_pulp3_hrefs_to_content_types_deb.rb +5 -0
  37. data/engines/bastion/app/assets/javascripts/bastion/auth/authorization.service.js +1 -1
  38. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/common/views/katello-agent-notice.html +1 -1
  39. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-system-purpose-modal.html +35 -40
  40. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-client.html +1 -1
  41. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +1 -1
  42. data/lib/katello/engine.rb +1 -1
  43. data/lib/katello/tasks/check_config.rake +18 -0
  44. data/lib/katello/tasks/delete_orphaned_content.rake +1 -3
  45. data/lib/katello/tasks/fix_hostgroup_facets.rake +8 -0
  46. data/lib/katello/tasks/pulp3_content_switchover.rake +7 -3
  47. data/lib/katello/tasks/pulp3_migration.rake +12 -3
  48. data/lib/katello/tasks/pulp3_migration_abort.rake +1 -1
  49. data/lib/katello/tasks/pulp3_migration_approve_corrupted.rake +16 -0
  50. data/lib/katello/tasks/pulp3_migration_reset.rake +26 -0
  51. data/lib/katello/tasks/pulp3_migration_stats.rake +37 -3
  52. data/lib/katello/tasks/pulp3_post_migration_check.rake +1 -3
  53. data/lib/katello/tasks/reimport.rake +1 -1
  54. data/lib/katello/tasks/reports.rake +4 -1
  55. data/lib/katello/tasks/repository.rake +3 -5
  56. data/lib/katello/version.rb +1 -1
  57. data/webpack/components/TypeAhead/TypeAhead.js +2 -1
  58. data/webpack/components/TypeAhead/pf3Search/TypeAheadSearch.js +2 -1
  59. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +7 -2
  60. data/webpack/scenes/Subscriptions/Manifest/index.js +1 -0
  61. metadata +28 -21
  62. data/lib/katello/tasks/common.rake +0 -7
@@ -31,43 +31,21 @@ module Katello
31
31
  validates :host, :presence => true, :allow_blank => false
32
32
  validates_with Validators::ContentViewEnvironmentValidator
33
33
 
34
- def bindable_types
35
- [
36
- {
37
- type: Repository::DEB_TYPE,
38
- matcher: '/pulp/deb/',
39
- paths: []
40
- },
41
- {
42
- type: Repository::YUM_TYPE,
43
- matcher: '/pulp/repos/',
44
- paths: []
45
- }
46
- ]
47
- end
48
-
49
34
  def update_repositories_by_paths(paths)
50
- bindable_paths = bindable_types
35
+ prefixes = %w(/pulp/deb/ /pulp/repos/ /pulp/content/)
51
36
  relative_paths = []
52
37
 
53
- # paths == ["/pulp/repos/Default_Organization/Library/custom/Test_product/test2",
54
- # "/pulp/repos/Default_Organization/Library/custom/Test_product/My_repo"]
55
- paths.each do |absolute_path|
56
- bindable_paths.each do |supported|
57
- relative_path = absolute_path.gsub(supported[:matcher], '') # remove e.g. '/pulp/repos/' from beginning of string
58
- relative_paths << relative_path unless relative_path == absolute_path
59
- if absolute_path.starts_with?(supported[:matcher])
60
- supported[:paths] << relative_path
61
- break
62
- end
38
+ # paths == ["/pulp/repos/Default_Organization/Library/custom/Test_product/test2"]
39
+ paths.each do |path|
40
+ if (prefix = prefixes.find { |pre| path.start_with?(pre) })
41
+ relative_paths << path.gsub(prefix, '')
42
+ else
43
+ Rails.logger.warn("System #{self.host.name} (#{self.host.id}) requested binding to repo with unknown prefix. #{path}")
63
44
  end
64
45
  end
65
46
 
66
- repos = bindable_paths.flat_map do |supported|
67
- repos = Repository.joins(:root).where(RootRepository.table_name => {content_type: supported[:type]}, relative_path: supported[:paths])
68
- relative_paths -= repos.pluck(:relative_path) # remove relative paths that match our repos
69
- repos
70
- end
47
+ repos = Repository.where(relative_path: relative_paths)
48
+ relative_paths -= repos.pluck(:relative_path) # remove relative paths that match our repos
71
49
 
72
50
  # Any leftover relative paths do not match the repos we've just retrieved from the db,
73
51
  # so we should log warnings about them.
@@ -23,25 +23,18 @@ module Katello
23
23
  services
24
24
  end
25
25
 
26
- # Calls "status" services in all backend engines.
27
26
  def ping(services: nil, capsule_id: nil)
28
- services ||= self.services(capsule_id)
29
- result = {}
30
- services.each { |service| result[service] = {} }
27
+ ping_services_for_capsule(services, capsule_id)
28
+ end
31
29
 
32
- ping_pulp3_without_auth(result[:pulp3], capsule_id) if result.include?(:pulp3)
33
- ping_pulp_without_auth(result[:pulp], capsule_id) if result.include?(:pulp)
34
- ping_candlepin_without_auth(result[:candlepin]) if result.include?(:candlepin)
30
+ def ping!(services: nil, capsule_id: nil)
31
+ result = ping_services_for_capsule(services, capsule_id)
35
32
 
36
- ping_pulp_with_auth(result[:pulp_auth], result[:pulp][:status]) if result.include?(:pulp_auth)
37
- ping_candlepin_with_auth(result[:candlepin_auth]) if result.include?(:candlepin_auth)
38
- ping_foreman_tasks(result[:foreman_tasks]) if result.include?(:foreman_tasks)
39
- ping_katello_events(result[:katello_events]) if result.include?(:katello_events)
40
- ping_candlepin_events(result[:candlepin_events]) if result.include?(:candlepin_events)
33
+ if result[:status] != OK_RETURN_CODE
34
+ failed_names = failed_services(result).keys
35
+ fail("The following services have not been started or are reporting errors: #{failed_names.join(', ')}")
36
+ end
41
37
 
42
- # set overall status result code
43
- result = {:services => result}
44
- result[:status] = result[:services].each_value.any? { |v| v[:status] == FAIL_RETURN_CODE } ? FAIL_RETURN_CODE : OK_RETURN_CODE
45
38
  result
46
39
  end
47
40
 
@@ -221,6 +214,33 @@ module Katello
221
214
 
222
215
  private
223
216
 
217
+ def failed_services(result)
218
+ result[:services].reject do |_name, details|
219
+ details[:status] != OK_RETURN_CODE
220
+ end
221
+ end
222
+
223
+ def ping_services_for_capsule(services, capsule_id)
224
+ services ||= self.services(capsule_id)
225
+ result = {}
226
+ services.each { |service| result[service] = {} }
227
+
228
+ ping_pulp3_without_auth(result[:pulp3], capsule_id) if result.include?(:pulp3)
229
+ ping_pulp_without_auth(result[:pulp], capsule_id) if result.include?(:pulp)
230
+ ping_candlepin_without_auth(result[:candlepin]) if result.include?(:candlepin)
231
+
232
+ ping_pulp_with_auth(result[:pulp_auth], result[:pulp][:status]) if result.include?(:pulp_auth)
233
+ ping_candlepin_with_auth(result[:candlepin_auth]) if result.include?(:candlepin_auth)
234
+ ping_foreman_tasks(result[:foreman_tasks]) if result.include?(:foreman_tasks)
235
+ ping_katello_events(result[:katello_events]) if result.include?(:katello_events)
236
+ ping_candlepin_events(result[:candlepin_events]) if result.include?(:candlepin_events)
237
+
238
+ # set overall status result code
239
+ result = {:services => result}
240
+ result[:status] = result[:services].each_value.any? { |v| v[:status] == FAIL_RETURN_CODE } ? FAIL_RETURN_CODE : OK_RETURN_CODE
241
+ result
242
+ end
243
+
224
244
  def fetch_proxy(capsule_id)
225
245
  capsule_id ? SmartProxy.unscoped.find(capsule_id) : SmartProxy.pulp_primary
226
246
  end
@@ -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
@@ -88,9 +78,37 @@ module Katello
88
78
  :bound_library_repos => self.bound_library_instance_repos).
89
79
  where("katello_host_installed_packages.host_id = :content_facet_id",
90
80
  :content_facet_id => self.content_facet.host.id).
91
- where("katello_module_stream_rpms.module_stream_id is null or
92
- katello_module_stream_rpms.module_stream_id in (:enabled_module_streams)",
93
- :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)
94
112
  end
95
113
 
96
114
  def newest_distinct_installed_packages_query
@@ -104,20 +122,22 @@ module Katello
104
122
  end
105
123
 
106
124
  def applicable_differences
107
- consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id")
108
- content_ids = fetch_content_ids
125
+ ActiveRecord::Base.connection.uncached do
126
+ consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id")
127
+ content_ids = fetch_content_ids
109
128
 
110
- to_remove = consumer_ids - content_ids
111
- to_add = content_ids - consumer_ids
129
+ to_remove = consumer_ids - content_ids
130
+ to_add = content_ids - consumer_ids
112
131
 
113
- [to_add, to_remove]
132
+ [to_add, to_remove]
133
+ end
114
134
  end
115
135
 
116
136
  def insert(applicable_ids)
117
137
  unless applicable_ids.empty?
118
138
  inserts = applicable_ids.map { |applicable_id| "(#{applicable_id.to_i}, #{content_facet.id.to_i})" }
119
139
  sql = "INSERT INTO #{content_facet_association_class.table_name} (#{content_unit_association_id}, content_facet_id) VALUES #{inserts.join(', ')}"
120
- ActiveRecord::Base.connection.execute(sql)
140
+ ActiveRecord::Base.connection.exec_insert(sql)
121
141
  end
122
142
  end
123
143
 
@@ -58,6 +58,20 @@ module Katello
58
58
  fail NotImplementedError
59
59
  end
60
60
 
61
+ def self.ignore_409_exception(*)
62
+ yield
63
+ rescue => e
64
+ raise e unless e&.code == 409
65
+ nil
66
+ end
67
+
68
+ def cancel_task(task_href)
69
+ data = PulpcoreClient::TaskResponse.new(state: 'canceled')
70
+ self.class.ignore_409_exception do
71
+ tasks_api.tasks_cancel(task_href, data)
72
+ end
73
+ end
74
+
61
75
  def exporter_api
62
76
  PulpcoreClient::ExportersPulpApi.new(core_api_client)
63
77
  end
@@ -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']
@@ -4,6 +4,8 @@ module Katello
4
4
  module Pulp3
5
5
  class Migration
6
6
  attr_accessor :smart_proxy, :reimport_all, :task_id
7
+ attr_reader :repository_types
8
+
7
9
  GET_QUERY_ID_LENGTH = 90
8
10
 
9
11
  MUTABLE_CONTENT_TYPES = [
@@ -15,6 +17,12 @@ module Katello
15
17
  Katello::Erratum
16
18
  ].freeze
17
19
 
20
+ CORRUPTABLE_CONTENT_TYPES = [
21
+ Katello::Rpm,
22
+ Katello::FileUnit,
23
+ Katello::Deb
24
+ ].freeze
25
+
18
26
  def self.repository_types_for_migration
19
27
  #we can migrate types that pulp3 supports, but are overridden to pulp2. These are in 'migration mode'
20
28
  overridden = (SETTINGS[:katello][:use_pulp_2_for_content_type] || {}).keys.select { |key| SETTINGS[:katello][:use_pulp_2_for_content_type][key] }
@@ -66,7 +74,9 @@ module Katello
66
74
 
67
75
  def last_successful_migration_time
68
76
  task = ForemanTasks::Task.where(:label => Actions::Pulp3::ContentMigration.to_s, :result => 'success').order("started_at desc").first
69
- if reimport_all || task.nil?
77
+ reset_task = ForemanTasks::Task.where(:label => Actions::Pulp3::ContentMigrationReset.to_s).order("started_at desc").first
78
+ reset_more_recent = reset_task && task && reset_task.started_at > task.started_at
79
+ if reimport_all || task.nil? || reset_more_recent
70
80
  0
71
81
  else
72
82
  task.started_at.to_i
@@ -104,6 +114,7 @@ module Katello
104
114
  Katello::RepositoryTypeManager.repository_types[repository_type_label].content_types_to_index.each do |content_type|
105
115
  Katello::Logging.time("CONTENT_MIGRATION - Importing Content", data: {type: content_type.label}) do
106
116
  import_content_type(content_type)
117
+ mark_missing_content(content_type)
107
118
  end
108
119
  end
109
120
  end
@@ -114,6 +125,45 @@ module Katello
114
125
  Katello::Pulp3::MigrationPlan.new(@repository_types).generate.as_json
115
126
  end
116
127
 
128
+ def reset
129
+ if @repository_types.empty?
130
+ fail ::Katello::Errors::Pulp3MigrationError, 'There are no Pulp 3 content types to reset'
131
+ end
132
+
133
+ plugins = @repository_types.sort.map do |repository_type|
134
+ {
135
+ type: ::Katello::Pulp3::MigrationPlan.pulp2_repository_type(repository_type)
136
+ }
137
+ end
138
+ plan = { plugins: plugins }
139
+
140
+ pulp3_task = migration_plan_api.reset(migration_plan_api.create(plan: plan).pulp_href)
141
+
142
+ content_types_for_migration.each do |content_type|
143
+ if content_type.model_class == ::Katello::Erratum
144
+ ::Katello::RepositoryErratum.update_all(erratum_pulp3_href: nil)
145
+ else
146
+ content_type.model_class.update_all(migrated_pulp3_href: nil, missing_from_migration: false, ignore_missing_from_migration: false)
147
+ end
148
+ end
149
+
150
+ @repository_types.each do |repo_type|
151
+ if repo_type == "file"
152
+ ::Katello::Repository.file_type.update_all(remote_href: nil, publication_href: nil, version_href: nil)
153
+ elsif repo_type == "docker"
154
+ ::Katello::Repository.docker_type.update_all(remote_href: nil, publication_href: nil, version_href: nil)
155
+ elsif repo_type == "yum"
156
+ ::Katello::Repository.yum_type.update_all(remote_href: nil, publication_href: nil, version_href: nil)
157
+ end
158
+ end
159
+
160
+ ::Katello::Pulp3::RepositoryReference.destroy_all
161
+ ::Katello::Pulp3::DistributionReference.destroy_all
162
+ ::Katello::Pulp3::ContentGuard.destroy_all
163
+
164
+ pulp3_task
165
+ end
166
+
117
167
  def create_migrations
118
168
  plan = migration_plan
119
169
  Rails.logger.info("Migration Plan: #{plan}")
@@ -127,7 +177,7 @@ module Katello
127
177
  end
128
178
 
129
179
  def start_migration(plan_href)
130
- migration_plan_api.run(plan_href, dry_run: false, validate: true)
180
+ migration_plan_api.run(plan_href, dry_run: false, validate: true, skip_corrupted: true)
131
181
  end
132
182
 
133
183
  def import_repositories(repository_type_label)
@@ -255,10 +305,17 @@ module Katello
255
305
  to_import[[errata_id, repo_id]] ||= {erratum_id: errata_id, erratum_pulp3_href: pulp3_href, repository_id: repo_id}
256
306
  end
257
307
  end
308
+
309
+ Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
310
+ on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
311
+ to_import = {}
258
312
  end
313
+ end
259
314
 
260
- Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
261
- on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
315
+ def mark_missing_content(content_type)
316
+ unless [Katello::DockerTag, Katello::DockerManifest, Katello::Erratum].include?(content_type.model_class)
317
+ content_type.model_class.where(:migrated_pulp3_href => nil).update_all(:missing_from_migration => true)
318
+ end
262
319
  end
263
320
 
264
321
  def import_content_type(content_type)
@@ -278,10 +335,9 @@ module Katello
278
335
  unmigrated_units.select(:id, :pulp_id).find_in_batches(batch_size: GET_QUERY_ID_LENGTH) do |needing_hrefs|
279
336
  current_count += needing_hrefs.count
280
337
  update_import_status("Importing migrated content type #{content_type.label}: #{current_count}/#{total_count}")
281
- migrated_units = pulp2_content_api.list(pulp2_id__in: needing_hrefs.map { |unit| unit.pulp_id }.join(','))
338
+ migrated_units = pulp2_content_api.list(pulp2_id__in: needing_hrefs.map(&:pulp_id))
282
339
  migrated_units.results.each do |migrated_unit|
283
- matching_record = needing_hrefs.find { |db_unit| db_unit.pulp_id == migrated_unit.pulp2_id }
284
- matching_record&.update_column(:migrated_pulp3_href, migrated_unit.pulp3_content)
340
+ content_type.model_class.where(pulp_id: migrated_unit.pulp2_id).update_all(migrated_pulp3_href: migrated_unit.pulp3_content)
285
341
  end
286
342
  end
287
343
  end
@@ -21,13 +21,13 @@ module Katello
21
21
  def generate_plugins
22
22
  @repository_types.sort.map do |repository_type|
23
23
  {
24
- type: pulp2_repository_type(repository_type),
24
+ type: self.class.pulp2_repository_type(repository_type),
25
25
  repositories: repository_migrations(repository_type)
26
26
  }
27
27
  end
28
28
  end
29
29
 
30
- def pulp2_repository_type(repository_type)
30
+ def self.pulp2_repository_type(repository_type)
31
31
  if repository_type == 'yum'
32
32
  return 'rpm' #migration plugin uses rpm
33
33
  else
@@ -21,6 +21,20 @@ module Katello
21
21
  Katello::Logging.time("CONTENT_SWITCHOVER - combine_duplicate_content_types") { combine_duplicate_content_types }
22
22
  Katello::Logging.time("CONTENT_SWITCHOVER - combine_duplicate_docker_tags") { combine_duplicate_docker_tags } if docker_migration?
23
23
  Katello::Logging.time("CONTENT_SWITCHOVER - migrate_pulp3_hrefs") { migrate_pulp3_hrefs }
24
+ Katello::Logging.time("CONTENT_SWITCHOVER - remove_missing_content") { remove_missing_content }
25
+ end
26
+ end
27
+
28
+ def remove_orphaned_content
29
+ models = []
30
+ @migration.repository_types.each do |repo_type_label|
31
+ repo_type = ::Katello::RepositoryTypeManager.repository_types[repo_type_label]
32
+ indexable_types = repo_type.content_types_to_index
33
+ models += indexable_types&.map(&:model_class)
34
+ models.select! { |model| model.many_repository_associations }
35
+ end
36
+ models.each do |model|
37
+ model.joins("left join katello_#{model.repository_association} on #{model.table_name}.id = katello_#{model.repository_association}.#{model.unit_id_field}").where("katello_#{model.repository_association}.#{model.unit_id_field} IS NULL").destroy_all
24
38
  end
25
39
  end
26
40
 
@@ -106,12 +120,29 @@ module Katello
106
120
  end
107
121
  end
108
122
 
109
- def migrated_content_type_check
123
+ def remove_missing_content
110
124
  content_types.each do |content_type|
111
- if content_type.model_class == Katello::Erratum
112
- migrated_errata_check
113
- elsif content_type.model_class.where(migrated_pulp3_href: nil).any?
114
- fail SwitchOverError, "ERROR: at least one #{content_type.model_class.table_name} record has migrated_pulp3_href NULL value\n"
125
+ if Migration::CORRUPTABLE_CONTENT_TYPES.include?(content_type.model_class)
126
+ content_type.model_class.ignored_missing_migrated_content.destroy_all
127
+ elsif content_type.model_class == Katello::Erratum
128
+ Katello::RepositoryErratum.where(:erratum_pulp3_href => nil).delete_all
129
+ else
130
+ content_type.model_class.unmigrated_content.destroy_all
131
+ end
132
+ end
133
+ end
134
+
135
+ def migrated_content_type_check
136
+ content_classes = content_types.map(&:model_class)
137
+ migrated_errata_check if content_classes.include?(Katello::Erratum)
138
+
139
+ (content_classes & Migration::CORRUPTABLE_CONTENT_TYPES).each do |content_type|
140
+ if content_type.missing_migrated_content.any?
141
+ fail SwitchOverError, "ERROR: at least one #{content_type.table_name} record has been detected as corrupt or missing. Run 'foreman-rake katello:pulp3_migration_stats' for more information.\n"
142
+ end
143
+
144
+ if content_type.unmigrated_content.any?
145
+ fail SwitchOverError, "ERROR: at least one #{content_type.table_name} record was not able to be migrated\n"
115
146
  end
116
147
  end
117
148
  end
@@ -1,3 +1,4 @@
1
+ # rubocop:disable Metrics/ClassLength
1
2
  require "pulpcore_client"
2
3
  module Katello
3
4
  module Pulp3
@@ -157,10 +158,7 @@ module Katello
157
158
  end
158
159
 
159
160
  def compute_remote_options(computed_options = remote_options)
160
- [:client_cert, :client_key, :ca_cert].each do |key|
161
- computed_options[key] = Digest::SHA256.hexdigest(computed_options[key].chomp) if computed_options[key]
162
- end
163
- computed_options.except(:name)
161
+ computed_options.except(:name, :client_key)
164
162
  end
165
163
 
166
164
  def create
@@ -220,11 +218,38 @@ module Katello
220
218
  end
221
219
 
222
220
  def refresh_distributions
223
- dist_ref = distribution_reference
224
- if dist_ref
225
- update_distribution
221
+ if repo.docker?
222
+ dist = lookup_distributions(base_path: repo.container_repository_name).first
226
223
  else
224
+ dist = lookup_distributions(base_path: repo.relative_path).first
225
+ end
226
+
227
+ # First check if the distribution exists
228
+ if dist
229
+ dist_ref = distribution_reference
230
+ # If we have a DistributionReference, update the distribution
231
+ if dist_ref
232
+ return update_distribution
233
+ # If no DistributionReference, create a DistributionReference and return
234
+ else
235
+ save_distribution_references([dist.pulp_href])
236
+ return []
237
+ end
238
+ end
239
+
240
+ # So far, it looks like there is no distribution. Try to create one.
241
+ begin
227
242
  create_distribution(relative_path)
243
+ rescue api.class.client_module::ApiError => e
244
+ # Now it seems there is a distribution. Fetch it and save the reference.
245
+ if e.message.include?("\"base_path\":[\"This field must be unique.\"]") ||
246
+ e.message.include?("\"base_path\":[\"Overlaps with existing distribution\"")
247
+ dist = lookup_distributions(base_path: repo.relative_path).first
248
+ save_distribution_references([dist.pulp_href])
249
+ return []
250
+ else
251
+ raise e
252
+ end
228
253
  end
229
254
  end
230
255
 
@@ -257,8 +282,12 @@ module Katello
257
282
  create_version(:base_version => from_repository.version_href)
258
283
  end
259
284
 
285
+ def version_zero?
286
+ repo.version_href.ends_with?('/versions/0/')
287
+ end
288
+
260
289
  def delete_version
261
- ignore_404_exception { api.repository_versions_api.delete(repo.version_href) }
290
+ ignore_404_exception { api.repository_versions_api.delete(repo.version_href) } unless version_zero?
262
291
  end
263
292
 
264
293
  def create_version(options = {})
@@ -270,7 +299,8 @@ module Katello
270
299
  pulp3_distribution_data = api.get_distribution(href)
271
300
  path, content_guard_href = pulp3_distribution_data&.base_path, pulp3_distribution_data&.content_guard
272
301
  unless distribution_reference
273
- DistributionReference.create!(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href)
302
+ # Ensure that duplicates won't be created in the case of a race condition
303
+ DistributionReference.where(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href).first_or_create!
274
304
  end
275
305
  end
276
306
  end
@@ -300,7 +330,7 @@ module Katello
300
330
  }
301
331
  remote_options[:url] = root.url unless root.url.blank?
302
332
  remote_options[:download_concurrency] = root.download_concurrency unless root.download_concurrency.blank?
303
- if root.upstream_username && root.upstream_password
333
+ if !root.upstream_username.blank? && !root.upstream_password.blank?
304
334
  remote_options.merge!(username: root.upstream_username,
305
335
  password: root.upstream_password)
306
336
  end