katello 4.12.0 → 4.13.0.rc1

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 (239) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/app/assets/javascripts/katello/locale/bn/katello.js +3365 -3350
  4. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +3136 -3121
  5. data/app/assets/javascripts/katello/locale/ca/katello.js +3588 -3576
  6. data/app/assets/javascripts/katello/locale/cs/katello.js +3499 -3487
  7. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +4186 -4186
  8. data/app/assets/javascripts/katello/locale/de/katello.js +5553 -5562
  9. data/app/assets/javascripts/katello/locale/de_AT/katello.js +3008 -2993
  10. data/app/assets/javascripts/katello/locale/de_DE/katello.js +3066 -3051
  11. data/app/assets/javascripts/katello/locale/el/katello.js +3376 -3370
  12. data/app/assets/javascripts/katello/locale/en/katello.js +3008 -2993
  13. data/app/assets/javascripts/katello/locale/en_GB/katello.js +3076 -3073
  14. data/app/assets/javascripts/katello/locale/en_US/katello.js +3008 -2993
  15. data/app/assets/javascripts/katello/locale/es/katello.js +5366 -5372
  16. data/app/assets/javascripts/katello/locale/et_EE/katello.js +3008 -2993
  17. data/app/assets/javascripts/katello/locale/fr/katello.js +5975 -5984
  18. data/app/assets/javascripts/katello/locale/gl/katello.js +3125 -3113
  19. data/app/assets/javascripts/katello/locale/gu/katello.js +3119 -3104
  20. data/app/assets/javascripts/katello/locale/he_IL/katello.js +3020 -3005
  21. data/app/assets/javascripts/katello/locale/hi/katello.js +3137 -3122
  22. data/app/assets/javascripts/katello/locale/id/katello.js +3008 -2993
  23. data/app/assets/javascripts/katello/locale/it/katello.js +4469 -4466
  24. data/app/assets/javascripts/katello/locale/ja/katello.js +5969 -5978
  25. data/app/assets/javascripts/katello/locale/ka/katello.js +5649 -5652
  26. data/app/assets/javascripts/katello/locale/kn/katello.js +3136 -3121
  27. data/app/assets/javascripts/katello/locale/ko/katello.js +4717 -4720
  28. data/app/assets/javascripts/katello/locale/locale/katello.js +1050 -1084
  29. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +3008 -2993
  30. data/app/assets/javascripts/katello/locale/mr/katello.js +3136 -3121
  31. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +3116 -3101
  32. data/app/assets/javascripts/katello/locale/or/katello.js +3137 -3122
  33. data/app/assets/javascripts/katello/locale/pa/katello.js +3136 -3121
  34. data/app/assets/javascripts/katello/locale/pl/katello.js +3210 -3195
  35. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +3008 -2993
  36. data/app/assets/javascripts/katello/locale/pt/katello.js +3009 -2994
  37. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +5362 -5368
  38. data/app/assets/javascripts/katello/locale/ro/katello.js +3008 -2993
  39. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +3008 -2993
  40. data/app/assets/javascripts/katello/locale/ru/katello.js +4638 -4641
  41. data/app/assets/javascripts/katello/locale/sl/katello.js +3051 -3036
  42. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +3156 -3144
  43. data/app/assets/javascripts/katello/locale/ta/katello.js +3365 -3350
  44. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +3121 -3106
  45. data/app/assets/javascripts/katello/locale/te/katello.js +3136 -3121
  46. data/app/assets/javascripts/katello/locale/tr/katello.js +3025 -3010
  47. data/app/assets/javascripts/katello/locale/vi/katello.js +3008 -2993
  48. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +3008 -2993
  49. data/app/assets/javascripts/katello/locale/zh/katello.js +3008 -2993
  50. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +5968 -5977
  51. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +4694 -4697
  52. data/app/assets/javascripts/katello/sync_management/sync_management.js +1 -0
  53. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +51 -124
  54. data/app/controllers/katello/api/rhsm/candlepin_dynflow_proxy_controller.rb +12 -20
  55. data/app/controllers/katello/api/v2/activation_keys_controller.rb +10 -4
  56. data/app/controllers/katello/api/v2/capsule_content_controller.rb +24 -0
  57. data/app/controllers/katello/api/v2/content_view_versions_controller.rb +9 -2
  58. data/app/controllers/katello/api/v2/debs_controller.rb +1 -1
  59. data/app/controllers/katello/api/v2/errata_controller.rb +1 -1
  60. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +12 -4
  61. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +3 -3
  62. data/app/controllers/katello/api/v2/organizations_controller.rb +0 -11
  63. data/app/controllers/katello/api/v2/packages_controller.rb +1 -1
  64. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -1
  65. data/app/controllers/katello/api/v2/repositories_controller.rb +18 -12
  66. data/app/controllers/katello/api/v2/repository_sets_controller.rb +2 -1
  67. data/app/controllers/katello/api/v2/simple_content_access_controller.rb +9 -22
  68. data/app/controllers/katello/concerns/api/v2/authorization.rb +1 -1
  69. data/app/helpers/katello/katello_urls_helper.rb +26 -1
  70. data/app/helpers/katello/subscription_mailer_helper.rb +1 -1
  71. data/app/jobs/create_manifest_expire_soon_warning_notifications.rb +11 -0
  72. data/app/lib/actions/candlepin/owner/regenerate_upstream_identity_cert.rb +21 -0
  73. data/app/lib/actions/katello/capsule_content/sync.rb +1 -1
  74. data/app/lib/actions/katello/capsule_content/verify_checksum.rb +75 -0
  75. data/app/lib/actions/katello/content_view/promote.rb +1 -1
  76. data/app/lib/actions/katello/content_view/publish.rb +1 -1
  77. data/app/lib/actions/katello/content_view_version/verify_checksum.rb +29 -0
  78. data/app/lib/actions/katello/host/hypervisors_update.rb +1 -0
  79. data/app/lib/actions/katello/host/update_content_view.rb +2 -2
  80. data/app/lib/actions/katello/organization/manifest_import.rb +5 -0
  81. data/app/lib/actions/katello/organization/manifest_refresh.rb +3 -0
  82. data/app/lib/actions/katello/repository/metadata_generate.rb +7 -1
  83. data/app/lib/actions/katello/repository/remove_content.rb +1 -0
  84. data/app/lib/actions/katello/repository/sync.rb +2 -1
  85. data/app/lib/actions/katello/repository/upload_files.rb +1 -0
  86. data/app/lib/actions/pulp3/capsule_content/verify_checksum.rb +27 -0
  87. data/app/lib/actions/pulp3/orchestration/content_view_version/export_repository.rb +7 -9
  88. data/app/lib/actions/pulp3/orchestration/content_view_version/syncable_export.rb +5 -4
  89. data/app/lib/katello/concerns/base_template_scope_extensions.rb +7 -2
  90. data/app/lib/katello/errors.rb +4 -0
  91. data/app/lib/katello/http_resource.rb +6 -1
  92. data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
  93. data/app/lib/katello/resources/candlepin/upstream_consumer.rb +18 -6
  94. data/app/lib/katello/resources/candlepin/upstream_job.rb +1 -1
  95. data/app/lib/katello/resources/registry.rb +25 -0
  96. data/app/mailers/katello/subscription_mailer.rb +3 -6
  97. data/app/models/katello/candlepin/repository_mapper.rb +1 -1
  98. data/app/models/katello/concerns/organization_extensions.rb +42 -3
  99. data/app/models/katello/content_view.rb +28 -0
  100. data/app/models/katello/content_view_environment_content_facet.rb +4 -2
  101. data/app/models/katello/glue/provider.rb +19 -12
  102. data/app/models/katello/glue/pulp/repos.rb +3 -8
  103. data/app/models/katello/host/content_facet.rb +1 -1
  104. data/app/models/katello/host/subscription_facet.rb +1 -1
  105. data/app/models/katello/host_collection.rb +12 -3
  106. data/app/models/katello/ping.rb +1 -1
  107. data/app/models/katello/repository.rb +33 -0
  108. data/app/models/katello/root_repository.rb +0 -4
  109. data/app/services/katello/content_unit_indexer.rb +9 -0
  110. data/app/services/katello/pulp3/alternate_content_source.rb +4 -6
  111. data/app/services/katello/pulp3/api/core.rb +13 -0
  112. data/app/services/katello/pulp3/api/yum.rb +11 -0
  113. data/app/services/katello/pulp3/docker_manifest.rb +5 -1
  114. data/app/services/katello/pulp3/repository/generic.rb +1 -1
  115. data/app/services/katello/pulp3/repository.rb +26 -6
  116. data/app/services/katello/pulp3/repository_mirror.rb +13 -12
  117. data/app/services/katello/pulp3/service_common.rb +2 -10
  118. data/app/services/katello/pulp3/smart_proxy_repository.rb +0 -2
  119. data/app/services/katello/ui_notifications/subscriptions/manifest_expire_soon_warning.rb +75 -0
  120. data/app/views/foreman/job_templates/update_package_-_katello_ansible_default.erb +5 -1
  121. data/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb +2 -2
  122. data/app/views/foreman/job_templates/upload_profile.erb +16 -0
  123. data/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl +9 -0
  124. data/app/views/katello/api/v2/docker_manifests/show.json.rabl +1 -0
  125. data/app/views/katello/api/v2/hosts/host_collections.json.rabl +5 -1
  126. data/app/views/katello/api/v2/organizations/show.json.rabl +9 -1
  127. data/app/views/katello/hosts/_errata_counts.html.erb +1 -1
  128. data/app/views/overrides/activation_keys/_host_environment_select.html.erb +1 -1
  129. data/app/views/overrides/activation_keys/_host_media_type_select.html.erb +15 -5
  130. data/app/views/overrides/activation_keys/_host_tab_pane.html.erb +1 -29
  131. data/config/routes/api/registry.rb +4 -8
  132. data/config/routes/api/v2.rb +2 -0
  133. data/db/migrate/20240423112842_add_fields_to_katello_docker_manifest.rb +8 -0
  134. data/db/migrate/20240502192021_change_katello_repository_rpms_id_seq_to_big_int.rb +9 -0
  135. data/db/seeds.d/109-katello-notification-blueprints.rb +6 -0
  136. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-repository-sets.controller.js +3 -3
  137. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-credentials/new/views/new-content-credential.html +2 -1
  138. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-repository-sets.controller.js +3 -3
  139. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +0 -15
  140. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +8 -6
  141. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +12 -10
  142. data/lib/katello/permission_creator.rb +3 -3
  143. data/lib/katello/permissions/registry_permissions.rb +4 -7
  144. data/lib/katello/plugin.rb +10 -9
  145. data/lib/katello/repository_types/ostree.rb +7 -0
  146. data/lib/katello/scheduled_jobs.rb +7 -1
  147. data/lib/katello/tasks/clean_backend_objects.rake +1 -1
  148. data/lib/katello/tasks/repository.rake +22 -0
  149. data/lib/katello/version.rb +1 -1
  150. data/locale/action_names.rb +4 -3
  151. data/locale/bn/katello.po +166 -151
  152. data/locale/bn_IN/katello.po +166 -151
  153. data/locale/ca/katello.po +166 -151
  154. data/locale/cs/katello.po +166 -151
  155. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  156. data/locale/cs_CZ/katello.po +172 -157
  157. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  158. data/locale/de/katello.po +178 -163
  159. data/locale/de_AT/katello.po +166 -151
  160. data/locale/de_DE/katello.po +166 -151
  161. data/locale/el/katello.po +166 -151
  162. data/locale/en/katello.po +166 -151
  163. data/locale/en_GB/katello.po +166 -151
  164. data/locale/en_US/katello.po +166 -151
  165. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  166. data/locale/es/katello.po +178 -163
  167. data/locale/et_EE/katello.po +166 -151
  168. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  169. data/locale/fr/katello.po +179 -164
  170. data/locale/gl/katello.po +166 -151
  171. data/locale/gu/katello.po +166 -151
  172. data/locale/he_IL/katello.po +166 -151
  173. data/locale/hi/katello.po +166 -151
  174. data/locale/id/katello.po +166 -151
  175. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  176. data/locale/it/katello.po +169 -154
  177. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  178. data/locale/ja/katello.po +179 -164
  179. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  180. data/locale/ka/katello.po +177 -162
  181. data/locale/katello.pot +1119 -1062
  182. data/locale/kn/katello.po +166 -151
  183. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  184. data/locale/ko/katello.po +174 -159
  185. data/locale/ml_IN/katello.po +166 -151
  186. data/locale/mr/katello.po +166 -151
  187. data/locale/nl_NL/katello.po +166 -151
  188. data/locale/or/katello.po +166 -151
  189. data/locale/pa/katello.po +166 -151
  190. data/locale/pl/katello.po +166 -151
  191. data/locale/pl_PL/katello.po +166 -151
  192. data/locale/pt/katello.po +166 -151
  193. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  194. data/locale/pt_BR/katello.po +178 -163
  195. data/locale/ro/katello.po +166 -151
  196. data/locale/ro_RO/katello.po +166 -151
  197. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  198. data/locale/ru/katello.po +171 -156
  199. data/locale/sl/katello.po +166 -151
  200. data/locale/sv_SE/katello.po +166 -151
  201. data/locale/ta/katello.po +166 -151
  202. data/locale/ta_IN/katello.po +166 -151
  203. data/locale/te/katello.po +166 -151
  204. data/locale/tr/katello.po +166 -151
  205. data/locale/vi/katello.po +166 -151
  206. data/locale/vi_VN/katello.po +166 -151
  207. data/locale/zh/katello.po +166 -151
  208. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  209. data/locale/zh_CN/katello.po +179 -164
  210. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  211. data/locale/zh_TW/katello.po +171 -156
  212. data/webpack/ForemanColumnExtensions/index.js +129 -0
  213. data/webpack/components/ActivationKeysSearch/ActivationKeysSearch.test.js +28 -0
  214. data/webpack/components/ActivationKeysSearch/index.js +222 -0
  215. data/webpack/components/Table/TableWrapper.js +14 -0
  216. data/webpack/components/extensions/HostDetails/ActionsBar/index.js +1 -1
  217. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +1 -1
  218. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +1 -0
  219. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +1 -0
  220. data/webpack/components/extensions/Hosts/ActionsBar/index.js +20 -1
  221. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/BulkChangeHostCVModal.js +220 -0
  222. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/actions.js +23 -0
  223. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/index.js +25 -0
  224. data/webpack/components/extensions/Hosts/BulkActions/__tests__/bulkChangeHostCVModal.test.js +133 -0
  225. data/webpack/global_index.js +19 -0
  226. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +6 -3
  227. data/webpack/scenes/Hosts/ChangeContentSource/actions.js +3 -1
  228. data/webpack/scenes/Hosts/ChangeContentSource/components/ContentSourceForm.js +63 -25
  229. data/webpack/scenes/Hosts/ChangeContentSource/index.js +24 -16
  230. data/webpack/scenes/RedHatRepositories/__tests__/__snapshots__/RedHatRepositoriesPage.test.js.snap +1 -0
  231. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +64 -5
  232. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +16 -13
  233. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +14 -8
  234. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +1 -0
  235. data/webpack/scenes/Subscriptions/components/SubscriptionsToolbar/SubscriptionsToolbar.js +1 -1
  236. metadata +61 -42
  237. data/app/assets/javascripts/katello/hosts/activation_key_edit.js +0 -167
  238. data/app/lib/actions/katello/host/upload_package_profile.rb +0 -45
  239. data/app/lib/actions/katello/host/upload_profiles.rb +0 -47
@@ -100,16 +100,55 @@ module Katello
100
100
  end
101
101
  end
102
102
 
103
- def manifest_expired?
104
- manifest_expiry = owner_details.dig(:upstreamConsumer, :idCert, :serial, :expiration)
103
+ def manifest_expiration_date(cached: true)
104
+ Rails.cache.fetch("#{self.label}_manifest_expiration_date", expires_in: 1.minute, force: !cached) do
105
+ unless manifest_imported?(cached: cached)
106
+ Rails.logger.error "Manifest not imported for organization #{self.label}"
107
+ return nil
108
+ end
109
+ manifest_expiry = owner_details.dig(:upstreamConsumer, :idCert, :serial, :expiration)
110
+
111
+ if manifest_expiry.present?
112
+ DateTime.parse(manifest_expiry)
113
+ else
114
+ Rails.logger.error "Unable to parse manifest expiration date from owner details"
115
+ nil
116
+ end
117
+ end
118
+ end
119
+
120
+ def manifest_expired?(cached: true)
121
+ manifest_expiry = manifest_expiration_date(cached: cached)
105
122
 
106
123
  if manifest_expiry
107
- DateTime.parse(manifest_expiry) < DateTime.now
124
+ manifest_expiry < DateTime.now
108
125
  else
109
126
  false
110
127
  end
111
128
  end
112
129
 
130
+ def manifest_expiring_soon?(days = Setting[:expire_soon_days])
131
+ return false if !manifest_imported? || manifest_expired?
132
+ manifest_expiry = manifest_expiration_date
133
+
134
+ if manifest_expiry
135
+ manifest_expiry < DateTime.now + days.days
136
+ else
137
+ false
138
+ end
139
+ end
140
+
141
+ def manifest_expire_days_remaining
142
+ manifest_expiry = manifest_expiration_date
143
+ return 0 if manifest_expired?
144
+
145
+ if manifest_expiry
146
+ (manifest_expiry - DateTime.now).to_i
147
+ else
148
+ 0
149
+ end
150
+ end
151
+
113
152
  def manifest_history
114
153
  imports.map { |i| OpenStruct.new(i) }
115
154
  end
@@ -634,6 +634,7 @@ module Katello
634
634
  check_ready_to_import!
635
635
  else
636
636
  fail _("Import-only content views can not be published directly") if import_only? && !syncable
637
+ check_repositories_blocking_publish!
637
638
  check_composite_action_allowed!(organization.library)
638
639
  check_docker_repository_names!([organization.library])
639
640
  check_orphaned_content_facets!(environments: self.environments)
@@ -642,6 +643,16 @@ module Katello
642
643
  true
643
644
  end
644
645
 
646
+ def check_repositories_blocking_publish!
647
+ blocking_tasks = repositories&.map { |repo| repo.blocking_task }&.compact
648
+
649
+ if blocking_tasks&.any?
650
+ errored_tasks = blocking_tasks.uniq.map { |task| "- #{Setting['foreman_url']}/foreman_tasks/tasks/#{task&.id}" }.join("\n")
651
+ fail _("Pending tasks detected in repositories of this content view. Please wait for the tasks: " +
652
+ errored_tasks + " before publishing.")
653
+ end
654
+ end
655
+
645
656
  def check_docker_repository_names!(environments)
646
657
  environments.each do |environment|
647
658
  repositories = []
@@ -789,6 +800,10 @@ module Katello
789
800
  repositories.any? { |repo| repo.last_indexed && repo.last_indexed > latest_version_object.created_at }
790
801
  end
791
802
 
803
+ def unpublishable?
804
+ default? || import_only? || generated?
805
+ end
806
+
792
807
  def needs_publish?
793
808
  #Returns
794
809
  # True:
@@ -808,7 +823,9 @@ module Katello
808
823
  # a) No changes were detected via audits *and*
809
824
  # Audit for CV publish exists (Audits haven't been cleaned up)
810
825
  # *and* applied_filters field is set(Published after upgrade)
826
+ # b) Default, import only and generated CVs can not be published, hence these will always return false.
811
827
  #
828
+ return false if unpublishable?
812
829
  return true unless latest_version_object
813
830
  return nil unless last_publish_task_success?
814
831
  return composite_cv_components_changed? if composite?
@@ -868,6 +885,17 @@ module Katello
868
885
  filters.present?
869
886
  end
870
887
 
888
+ def blocking_task
889
+ blocking_task_labels = [
890
+ ::Actions::Katello::ContentView::Publish.name
891
+ ]
892
+ ForemanTasks::Task::DynflowTask.where(:label => blocking_task_labels)
893
+ .where.not(state: 'stopped')
894
+ .for_resource(self)
895
+ .order(:started_at)
896
+ .last
897
+ end
898
+
871
899
  protected
872
900
 
873
901
  def remove_repository(repository)
@@ -5,7 +5,7 @@ module Katello
5
5
 
6
6
  validates :content_view_environment_id, presence: true
7
7
  validates :content_facet_id, presence: true, unless: :new_record?
8
- validate :ensure_valid_content_source
8
+ validate :ensure_valid_content_source, if: proc { Setting['validate_host_lce_content_source_coherence'] }
9
9
 
10
10
  def ensure_valid_content_source
11
11
  source = self.content_facet&.content_source
@@ -14,7 +14,9 @@ module Katello
14
14
  hostname = self.content_facet&.host&.name
15
15
  return unless [source, env].all? { |x| x.present? }
16
16
  unless source.lifecycle_environments.include?(env)
17
- errors.add(:base, _("Host %{hostname}: Cannot add content view environment to content facet. The host's content source '%{content_source}' does not sync lifecycle environment '%{lce}'.") % { hostname: hostname, content_source: source.name, lce: env.name })
17
+ error_msg = _("Host %{hostname}: Cannot add content view environment to content facet. The host's content source '%{content_source}' does not sync lifecycle environment '%{lce}'.") % { hostname: hostname, content_source: source.name, lce: env.name }
18
+ Rails.logger.warn error_msg
19
+ errors.add(:base, error_msg)
18
20
  end
19
21
  end
20
22
  end
@@ -32,6 +32,10 @@ module Katello
32
32
 
33
33
  module InstanceMethods
34
34
  API_URL = 'https://subscription.rhsm.redhat.com/subscription/consumers/'.freeze
35
+ def api_url(upstream = {})
36
+ # Default to Red Hat
37
+ upstream['apiUrl'] || API_URL
38
+ end
35
39
 
36
40
  def sync
37
41
  Rails.logger.debug "Syncing provider #{name}"
@@ -62,34 +66,37 @@ module Katello
62
66
  fail _("Upstream identity certificate not available")
63
67
  end
64
68
 
65
- # Default to Red Hat
66
- url = upstream['apiUrl'] || API_URL
67
-
68
69
  params = {}
69
70
  params[:capabilities] = Resources::Candlepin::CandlepinPing.ping['managerCapabilities'].inject([]) do |result, element|
70
71
  result << {'name' => element}
71
72
  end
72
73
  params[:facts] = {:distributor_version => DISTRIBUTOR_VERSION }
73
- Resources::Candlepin::UpstreamConsumer.update("#{url}#{upstream['uuid']}", upstream['idCert']['cert'],
74
+ Resources::Candlepin::UpstreamConsumer.update("#{api_url(upstream)}#{upstream['uuid']}", upstream['idCert']['cert'],
74
75
  upstream['idCert']['key'], ca_file, params)
75
76
  end
76
77
 
77
- def start_owner_upstream_export(upstream)
78
+ def owner_upstream_regenerate_identity_cert(upstream)
78
79
  validate_upstream_identity_cert!(upstream)
79
- url = upstream['apiUrl'] || API_URL
80
+ Rails.logger.debug "Sending request to regenerate identity certificate for upstream consumer: #{upstream['uuid']}"
81
+ response = Resources::Candlepin::UpstreamConsumer.regenerate_upstream_identity("#{api_url(upstream)}#{upstream['uuid']}", upstream['idCert']['cert'],
82
+ upstream['idCert']['key'], ca_file)
83
+ JSON.parse(response)
84
+ end
80
85
 
81
- response = Resources::Candlepin::UpstreamConsumer.get_export("#{url}#{upstream['uuid']}/export/async", upstream['idCert']['cert'],
86
+ def start_owner_upstream_export(upstream)
87
+ validate_upstream_identity_cert!(upstream)
88
+ response = Resources::Candlepin::UpstreamConsumer.start_upstream_export("#{api_url(upstream)}#{upstream['uuid']}/export/async", upstream['idCert']['cert'],
82
89
  upstream['idCert']['key'], ca_file)
83
90
  JSON.parse(response)
84
91
  end
85
92
 
86
93
  def retrieve_owner_upstream_export(upstream, zip_file_path, export_id)
87
94
  validate_upstream_identity_cert!(upstream)
88
- url = upstream['apiUrl'] || API_URL
89
-
90
- data = Resources::Candlepin::UpstreamConsumer.get_export("#{url}#{upstream['uuid']}/export/#{export_id}", upstream['idCert']['cert'],
91
- upstream['idCert']['key'], ca_file)
92
-
95
+ data = Resources::Candlepin::UpstreamConsumer.retrieve_upstream_export(
96
+ "#{api_url(upstream)}#{upstream['uuid']}/export/#{export_id}",
97
+ upstream['idCert']['cert'],
98
+ upstream['idCert']['key'], ca_file
99
+ )
93
100
  File.write(zip_file_path, data, mode: 'wb')
94
101
 
95
102
  true
@@ -6,12 +6,6 @@ module Katello
6
6
  base.send :include, InstanceMethods
7
7
  end
8
8
 
9
- def self.repo_path_from_content_path(environment, content_path)
10
- path = content_path.sub(%r|^/|, '')
11
- path_prefix = [environment.organization.label, environment.label].join('/')
12
- "#{path_prefix}/#{path}"
13
- end
14
-
15
9
  module InstanceMethods
16
10
  def distributions(env)
17
11
  to_ret = []
@@ -56,7 +50,7 @@ module Katello
56
50
  end
57
51
 
58
52
  def last_sync_audit
59
- Audited::Audit.where(:auditable_id => self.repositories, :auditable_type => Katello::Repository.name).order(:created_at).last
53
+ Audited::Audit.where(:auditable_id => self.repositories, :auditable_type => Katello::Repository.name, :action => "sync").order(:created_at).last
60
54
  end
61
55
 
62
56
  def last_sync
@@ -64,13 +58,14 @@ module Katello
64
58
  end
65
59
 
66
60
  def last_repo_sync_task
67
- @last_sync_task ||= last_repo_sync_tasks.first
61
+ @last_sync_task ||= last_repo_sync_tasks&.first
68
62
  end
69
63
 
70
64
  def last_repo_sync_tasks
71
65
  ids = repos(self.library, nil, false).pluck(:id).join(',')
72
66
  label = ::Actions::Katello::Repository::Sync.name
73
67
  type = ::Katello::Repository.name
68
+ return nil if ids.empty?
74
69
  ForemanTasks::Task.search_for("label = #{label} and resource_type = #{type} and resource_id ^ (#{ids})")
75
70
  .order("started_at desc")
76
71
  end
@@ -44,7 +44,7 @@ module Katello
44
44
 
45
45
  validates_with ::AssociationExistsValidator, attributes: [:content_source]
46
46
  validates_with Katello::Validators::GeneratedContentViewValidator
47
- validates_associated :content_view_environment_content_facets
47
+ validates_associated :content_view_environment_content_facets, :message => _("invalid: The content source must sync the lifecycle environment assigned to the host. See the logs for more information.")
48
48
  validates :host, :presence => true, :allow_blank => false
49
49
 
50
50
  attr_accessor :cves_changed
@@ -67,7 +67,7 @@ module Katello
67
67
  self.purpose_addon_ids = consumer_params['addOns'].map { |addon_name| ::Katello::PurposeAddon.find_or_create_by(name: addon_name).id }
68
68
  end
69
69
 
70
- unless consumer_params['releaseVer'].blank?
70
+ unless consumer_params['releaseVer'].nil?
71
71
  release = consumer_params['releaseVer']
72
72
  release = release['releaseVer'] if release.is_a?(Hash)
73
73
  self.release_version = release
@@ -29,7 +29,10 @@ module Katello
29
29
  scoped_search :relation => :hosts, :on => :name, :rename => :host, :complete_value => true
30
30
 
31
31
  def max_hosts_check
32
- if !unlimited_hosts && (hosts.length > 0 && (hosts.length.to_i > max_hosts.to_i)) && max_hosts_changed?
32
+ # NOTE: max_hosts_check and max_hosts_no_exceeded use size() instead of count() because
33
+ # the host list exists as an array rather than a DB query when run as a validation.
34
+ host_count = hosts.size
35
+ if !unlimited_hosts && (host_count > 0 && (host_count.to_i > max_hosts.to_i)) && max_hosts_changed?
33
36
  errors.add :max_host, N_("may not be less than the number of hosts associated with the host collection.")
34
37
  end
35
38
  end
@@ -60,8 +63,14 @@ module Katello
60
63
  type ? query.of_type(type) : query
61
64
  end
62
65
 
63
- def total_hosts
64
- hosts.length
66
+ def cache_key
67
+ "#{self.class.name}/#{self.id}"
68
+ end
69
+
70
+ def total_hosts(cached: false)
71
+ Rails.cache.fetch("#{cache_key}/total_hosts", expires_in: 1.minute, force: !cached) do
72
+ hosts.count
73
+ end
65
74
  end
66
75
 
67
76
  # Retrieve the list of accessible host collections in the organization specified, returning
@@ -2,7 +2,7 @@ module Katello
2
2
  class Ping
3
3
  OK_RETURN_CODE = 'ok'.freeze
4
4
  FAIL_RETURN_CODE = 'FAIL'.freeze
5
- PACKAGES = %w(katello candlepin pulp foreman hammer).freeze
5
+ PACKAGES = %w(katello candlepin pulp foreman hammer dynflow).freeze
6
6
 
7
7
  class << self
8
8
  def services(capsule_id = nil)
@@ -201,6 +201,12 @@ module Katello
201
201
  joins(:root).where("#{Katello::RootRepository.table_name}.product_id" => products)
202
202
  end
203
203
 
204
+ def self.repo_path_from_content_path(environment, content_path)
205
+ path = content_path.sub(%r|^/|, '')
206
+ path_prefix = [environment.organization.label, environment.label].join('/')
207
+ "#{path_prefix}/#{path}"
208
+ end
209
+
204
210
  def to_label
205
211
  name
206
212
  end
@@ -645,6 +651,33 @@ module Katello
645
651
  for_resource(self).order(:started_at).last
646
652
  end
647
653
 
654
+ def blocking_task
655
+ blocking_task_labels = [
656
+ ::Actions::Katello::Repository::Sync.name,
657
+ ::Actions::Katello::Repository::UploadFiles.name,
658
+ ::Actions::Katello::Repository::RemoveContent.name,
659
+ ::Actions::Katello::Repository::MetadataGenerate.name
660
+ ]
661
+ ForemanTasks::Task::DynflowTask.where(:label => blocking_task_labels)
662
+ .where.not(state: 'stopped')
663
+ .for_resource(self)
664
+ .order(:started_at)
665
+ .last
666
+ end
667
+
668
+ def check_ready_to_act!
669
+ blocking_tasks = content_views&.map { |cv| cv.blocking_task }&.compact
670
+
671
+ if blocking_tasks&.any?
672
+ errored_tasks = blocking_tasks
673
+ .uniq
674
+ .map { |task| "- #{Setting['foreman_url']}/foreman_tasks/tasks/#{task&.id}" }
675
+ .join("\n")
676
+ fail _("This repository has pending tasks in associated content views. Please wait for the tasks: " + errored_tasks +
677
+ " to complete before proceeding.")
678
+ end
679
+ end
680
+
648
681
  # returns other instances of this repo with the same library
649
682
  # equivalent of repo
650
683
  def environmental_instances(view)
@@ -387,10 +387,6 @@ module Katello
387
387
  Katello::RepositoryTypeManager.generic_repository_types(false).values.map(&:id).map(&:to_s).flatten.include? self.content_type
388
388
  end
389
389
 
390
- def metadata_generate_needed?
391
- (%w(unprotected checksum_type container_repsoitory_name) & previous_changes.keys).any?
392
- end
393
-
394
390
  def using_mirrored_content?
395
391
  self.mirroring_policy != Katello::RootRepository::MIRRORING_POLICY_ADDITIVE
396
392
  end
@@ -55,6 +55,15 @@ module Katello
55
55
  end
56
56
  end
57
57
 
58
+ def reimport_units
59
+ units_from_pulp.each do |units|
60
+ to_update = units.map do |unit|
61
+ @service_class.generate_model_row(unit)
62
+ end
63
+ @model_class.upsert_all(to_update, unique_by: :pulp_id)
64
+ end
65
+ end
66
+
58
67
  def import_associations(units)
59
68
  pulp_id_to_id = self.class.pulp_id_to_id_map(@content_type, units.map { |unit| unit[@service_class.unit_identifier] })
60
69
  @service_class.insert_child_associations(units, pulp_id_to_id) if @service_class.respond_to?(:insert_child_associations)
@@ -88,18 +88,16 @@ module Katello
88
88
  end
89
89
 
90
90
  def get_remote(href = smart_proxy_acs.remote_href)
91
- acs.base_url&.start_with?('uln') ? api.remotes_uln_api.read(href) : api.remotes_api.read(href)
91
+ api.get_remotes_api(href: href).read(href)
92
92
  end
93
93
 
94
- def update_remote
95
- api.remotes_api.partial_update(smart_proxy_acs.remote_href, remote_options)
94
+ def update_remote(href = smart_proxy_acs.remote_href)
95
+ api.get_remotes_api(href: href).partial_update(href, remote_options)
96
96
  end
97
97
 
98
- # The old repo URL is needed to determine which remote API to use.
99
98
  def delete_remote(options = {})
100
99
  options[:href] ||= smart_proxy_acs.remote_href
101
- options[:old_url] ||= remote_options[:url]
102
- ignore_404_exception { options[:old_url]&.start_with?('uln') ? api.remotes_uln_api.delete(options[:href]) : api.remotes_api.delete(options[:href]) } if options[:href]
100
+ ignore_404_exception { api.get_remotes_api(href: options[:href]).delete(options[:href]) } if options[:href]
103
101
  end
104
102
 
105
103
  def create
@@ -47,6 +47,11 @@ module Katello
47
47
  fail NotImplementedError
48
48
  end
49
49
 
50
+ # Method is called with either :url or :href parameters for the sake of yum content.
51
+ def get_remotes_api(*)
52
+ remotes_api
53
+ end
54
+
50
55
  def publications_api
51
56
  repository_type.publications_api_class.new(api_client) #Optional
52
57
  end
@@ -136,6 +141,10 @@ module Katello
136
141
  client
137
142
  end
138
143
 
144
+ def repair_api
145
+ PulpcoreClient::RepairApi.new(core_api_client)
146
+ end
147
+
139
148
  def uploads_api
140
149
  PulpcoreClient::UploadsApi.new(core_api_client)
141
150
  end
@@ -230,6 +239,10 @@ module Katello
230
239
  end
231
240
  end
232
241
 
242
+ def repair
243
+ repair_api.post(PulpcoreClient::Repair.new(verify_checksums: true))
244
+ end
245
+
233
246
  def self.fetch_from_list
234
247
  page_size = Setting[:bulk_load_size]
235
248
  page_opts = { "offset" => 0, limit: page_size }
@@ -32,6 +32,17 @@ module Katello
32
32
  PulpRpmClient::RemotesUlnApi.new(api_client)
33
33
  end
34
34
 
35
+ def get_remotes_api(href: nil, url: nil)
36
+ fail 'Provide exactly one of href or url for yum remote selection!' if url.blank? && href.blank?
37
+ fail 'The href must be a pulp_rpm remote href!' if href && !href.start_with?('/pulp/api/v3/remotes/rpm/')
38
+
39
+ if href&.start_with?('/pulp/api/v3/remotes/rpm/uln/') || url&.start_with?('uln')
40
+ remotes_uln_api
41
+ else
42
+ remotes_api
43
+ end
44
+ end
45
+
35
46
  def copy_api
36
47
  PulpRpmClient::RpmCopyApi.new(api_client)
37
48
  end
@@ -26,7 +26,11 @@ module Katello
26
26
  {
27
27
  schema_version: unit['schema_version'],
28
28
  digest: unit['digest'],
29
- pulp_id: unit[unit_identifier]
29
+ pulp_id: unit[unit_identifier],
30
+ annotations: unit['annotations'],
31
+ labels: unit['labels'],
32
+ is_bootable: unit['is_bootable'],
33
+ is_flatpak: unit['is_flatpak']
30
34
  }
31
35
  end
32
36
  end
@@ -3,7 +3,7 @@ module Katello
3
3
  class Repository
4
4
  class Generic < ::Katello::Pulp3::Repository
5
5
  def copy_content_for_source(source_repository, _options = {})
6
- copy_units_by_href(source_repository.files.pluck(:pulp_id))
6
+ copy_units_by_href(source_repository.generic_content_units&.pluck(:pulp_id))
7
7
  end
8
8
 
9
9
  def distribution_options(path)
@@ -91,16 +91,21 @@ module Katello
91
91
  end
92
92
 
93
93
  def remote_partial_update
94
- if remote_options[:url]&.start_with?('uln')
95
- api.remotes_uln_api.partial_update(repo.remote_href, remote_options)
96
- else
97
- api.remotes_api.partial_update(repo.remote_href, remote_options)
94
+ url_type = remote_options[:url]&.start_with?('uln') ? 'uln' : 'default'
95
+ remote_type = repo.remote_href.start_with?('/pulp/api/v3/remotes/rpm/uln/') ? 'uln' : 'default'
96
+ href = repo.remote_href
97
+
98
+ if url_type == remote_type
99
+ api.get_remotes_api(href: href).partial_update(href, remote_options)
100
+ else # We need to recreate a remote of the correct type!
101
+ create_remote
102
+ delete_remote(href: href)
98
103
  end
99
104
  end
100
105
 
101
106
  def delete_remote(options = {})
102
107
  options[:href] ||= repo.remote_href
103
- ignore_404_exception { remote_options[:url]&.start_with?('uln') ? api.remotes_uln_api.delete(options[:href]) : api.remotes_api.delete(options[:href]) } if options[:href]
108
+ ignore_404_exception { api.get_remotes_api(href: options[:href]).delete(options[:href]) } if options[:href]
104
109
  end
105
110
 
106
111
  def self.instance_for_type(repo, smart_proxy)
@@ -139,7 +144,7 @@ module Katello
139
144
  end
140
145
 
141
146
  def get_remote(href = repo.remote_href)
142
- repo.url&.start_with?('uln') ? api.remotes_uln_api.read(href) : api.remotes_api.read(href)
147
+ api.get_remotes_api(href: href).read(href)
143
148
  end
144
149
 
145
150
  def get_distribution(href = distribution_reference.href)
@@ -210,6 +215,10 @@ module Katello
210
215
  api.publications_api.create(publication_data)
211
216
  end
212
217
 
218
+ def delete_publication
219
+ ignore_404_exception { api.publications_api.delete(repo.publication_href) } if repo.publication_href
220
+ end
221
+
213
222
  def publication_options(repository_version)
214
223
  {
215
224
  repository_version: repository_version
@@ -329,6 +338,17 @@ module Katello
329
338
 
330
339
  def delete_version
331
340
  ignore_404_exception { api.repository_versions_api.delete(repo.version_href) } unless version_zero?
341
+ rescue api.api_exception_class => e
342
+ if e.message.include?("are currently being used to distribute content")
343
+ Rails.logger.warn "Exception when calling repository_versions_api->delete: #{e}"
344
+ publication_href = repo.publication_href
345
+ Rails.logger.warn "Trying to delete publication #{publication_href} for repository #{repo.id}}"
346
+ Rails.logger.error "Could not delete version: #{repo.version_href} because conflicting publication could not be looked up" unless publication_href
347
+ if publication_href
348
+ ignore_404_exception { api.publications_api.delete(publication_href) }
349
+ ignore_404_exception { api.repository_versions_api.delete(repo.version_href) }
350
+ end
351
+ end
332
352
  end
333
353
 
334
354
  def create_version(options = {})
@@ -24,11 +24,9 @@ module Katello
24
24
  def refresh_entities
25
25
  href = remote_href
26
26
  if href
27
- if remote_options[:url]&.start_with?('uln')
28
- [api.remotes_uln_api.partial_update(href, remote_options)]
29
- else
30
- [api.remotes_api.partial_update(href, remote_options)]
31
- end
27
+ # Do not consider remotes_uln_api, since the Katello server is not a ULN server. Even if the sync
28
+ # to Katello used ULN, the sync from Katello server to smart proxy will use a normal RPM remote!
29
+ [api.remotes_api.partial_update(href, remote_options)]
32
30
  else
33
31
  create_remote
34
32
  []
@@ -111,13 +109,10 @@ module Katello
111
109
  end
112
110
 
113
111
  def create_remote
114
- if remote_options[:url]&.start_with?('uln')
115
- remote_file_data = @repo_service.api.class.remote_uln_class.new(remote_options)
116
- api.remotes_uln_api.create(remote_file_data)
117
- else
118
- remote_file_data = @repo_service.api.remote_class.new(remote_options)
119
- api.remotes_api.create(remote_file_data)
120
- end
112
+ # Do not consider remotes_uln_api, since the Katello server is not a ULN server. Even if the sync
113
+ # to Katello used ULN, the sync from Katello server to smart proxy will use a normal RPM remote!
114
+ remote_file_data = @repo_service.api.remote_class.new(remote_options)
115
+ api.remotes_api.create(remote_file_data)
121
116
  end
122
117
 
123
118
  def compute_remote_options
@@ -240,6 +235,12 @@ module Katello
240
235
  distribution_data = api.distribution_class.new(distribution_options(path))
241
236
  repo_service.distributions_api.create(distribution_data)
242
237
  end
238
+
239
+ def repair
240
+ data = api.repair_class.new
241
+ fail "Could not lookup a version_href for repo #{repo_service.repo.id}" if version_href.nil?
242
+ api.repository_versions_api.repair(version_href, data)
243
+ end
243
244
  end
244
245
  end
245
246
  end
@@ -9,11 +9,7 @@ module Katello
9
9
  remote_file_data = api.remote_class.new(remote_options)
10
10
  end
11
11
  reformat_api_exception do
12
- if remote_options[:url]&.start_with?('uln')
13
- response = api.remotes_uln_api.create(remote_file_data)
14
- else
15
- response = api.remotes_api.create(remote_file_data)
16
- end
12
+ response = api.get_remotes_api(url: remote_options[:url]).create(remote_file_data)
17
13
  end
18
14
  response
19
15
  end
@@ -37,11 +33,7 @@ module Katello
37
33
  end
38
34
 
39
35
  reformat_api_exception do
40
- if remote_options[:url]&.start_with?('uln')
41
- response = api.remotes_uln_api.create(remote_file_data)
42
- else
43
- response = api.remotes_api.create(remote_file_data)
44
- end
36
+ response = api.get_remotes_api(url: remote_options[:url]).create(remote_file_data)
45
37
  #delete is async, but if its not properly deleted, orphan cleanup will take care of it later
46
38
  delete_remote(href: response.pulp_href)
47
39
  end
@@ -35,13 +35,11 @@ module Katello
35
35
 
36
36
  def delete_orphan_repository_versions
37
37
  tasks = []
38
-
39
38
  orphan_repository_versions.each do |api, version_hrefs|
40
39
  tasks << version_hrefs.collect do |href|
41
40
  api.repository_versions_api.delete(href)
42
41
  end
43
42
  end
44
-
45
43
  tasks.flatten
46
44
  end
47
45