katello 3.18.1 → 3.18.3.1

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 (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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78239ba1e921e408588db439d4b929151e1a9c10408e23c63f0ec5c76a23c66b
4
- data.tar.gz: f945779353ab8cedce6a30e33f5ac4a264f6790e486a00126f15d3862babdfce
3
+ metadata.gz: c9aff3d142eea532e4af243e46c2b98a4cc1016a60f652d2dd9514dc9f416371
4
+ data.tar.gz: ea80686280189f05d2ffcbfe8e3cf0406fa2b985082b43e15338f62bca099209
5
5
  SHA512:
6
- metadata.gz: f5526ccfbae423e0315e89349253756acf60bb920c9e3f1e2b7f581f677312e19d0141a185f58adeea0b08dc4d1a2d323bd051404c6c0d54faedd06ed5667427
7
- data.tar.gz: 5f7d476d2061439219dc468b5ead1291835fe68bfacf1396b54db46f3e9afe3ca13c3383e5d5ce92f1d07c76ab5a0f59cffa78cda0c9500345dc5813c6ea24dc
6
+ metadata.gz: b569aa5d2a488f3f248c4e29dfaf4c51ad1c014b9bcd2c20bf1c99f09f27d57b4490f1e8fd9c5f78b65a1f584e3f2f1262b91f6d0b57cb9c487781176a16de52
7
+ data.tar.gz: d0f344773b0172a608ca677effbb942fbf2b62386cf1179aefe3e8faf7fa5e0c16940cffb834a3e7be3007c6314525a09883989b51e714191160baa4dba65470
@@ -163,78 +163,6 @@ input:focus {
163
163
  @extend .status_exclamation_icon;
164
164
  }
165
165
 
166
- /* BUTTONS */
167
- input[type='submit'], button, .button {
168
- font-size: 10px;
169
- display: inline-block;
170
- vertical-align: bottom;
171
- background: -moz-linear-gradient(top, #f9f9f9, #f0f0f0, #e5e5e5, #e9e9e9);
172
- background: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), color-stop(0.9, #e5e5e5), to(#e9e9e9));
173
- box-shadow: none;
174
- border: 1px solid darken($stroke_color, 20%);
175
- color: #221e1f;
176
- cursor: pointer;
177
- padding: 4px 8px;
178
- border-radius: 5px;
179
- text-shadow: 0 1px 0 rgba($white_color, 1);
180
- min-height: 14px;
181
- .nomargin {
182
- margin: 0;
183
- }
184
- &:hover {
185
- background: -moz-linear-gradient(top, $white_color, $white_color, #cfcfcf);
186
- background: -webkit-gradient(linear, left top, left bottom, from($white_color), color-stop(0.6, $white_color), to(#cfcfcf));
187
- box-shadow: 0 1px 2px rgba(0,0,0,0.5);
188
- text-decoration: none;
189
- color: black;
190
- }
191
- &:active {
192
- background: -moz-linear-gradient(top, #c2c3c0, #e4e5e4);
193
- background: -webkit-gradient(linear, left top, left bottom, from(#c2c3c0), to(#e4e5e4));
194
- box-shadow: none;
195
- text-decoration: none;
196
- }
197
- &:focus {
198
- text-decoration: none;
199
- color: #000;
200
- border-width: 2px;
201
- }
202
- &.dialogbutton {
203
- float: right;
204
- margin-left: 3px;
205
- margin: 40px 4px 4px;
206
- }
207
- &.formbutton {
208
- display: inline-block;
209
- margin-left: 3px;
210
- margin: 40px 4px 4px;
211
- }
212
- &.actionlink {
213
- margin: 40px 0 0;
214
- }
215
- &.disabled, &[disabled] {
216
- cursor: default;
217
- background: transparent;
218
- opacity: 0.4;
219
-
220
- &:hover {
221
- background: transparent;
222
- box-shadow: none;
223
- }
224
- }
225
- &.iconbutton {
226
- display: inline-block;
227
- }
228
- &.tiny {
229
- padding: 4px;
230
- margin: 0;
231
- &:active, &:focus {
232
- margin: 0;
233
- padding: 3px;
234
- }
235
- }
236
- }
237
-
238
166
  table {
239
167
  border-collapse: collapse;
240
168
  border: 1px solid $stroke_color;
@@ -167,11 +167,9 @@ module Katello
167
167
  end
168
168
 
169
169
  if (manifest_response = redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers) })
170
- #when pulp 2 is removed, this should no longer be needed, and all clients should be redirected
171
- logger.debug filter_sensitive_data(manifest_response)
170
+ #for some requests, we get a redirect, but for others we get the actual manifest in response
172
171
  results = JSON.parse(manifest_response)
173
-
174
- response.header['Docker-Content-Digest'] = "sha256:#{Digest::SHA256.hexdigest(manifest_response)}"
172
+ response.header['Docker-Content-Digest'] = manifest_response.headers[:docker_content_digest]
175
173
  # https://docs.docker.com/registry/spec/manifest-v2-2/
176
174
  # If its v2 schema 2 only the mediaType attribute will be present in the manifest
177
175
  media_type = results['mediaType']
@@ -184,8 +182,8 @@ module Katello
184
182
  'application/vnd.docker.distribution.manifest.v1+prettyjws'
185
183
  end
186
184
  end
187
-
188
- render json: manifest_response, content_type: media_type
185
+ response.headers['Content-Type'] = media_type
186
+ render json: manifest_response
189
187
  end
190
188
  end
191
189
 
@@ -39,8 +39,7 @@ module Katello
39
39
  end
40
40
 
41
41
  def deprecate_katello_agent
42
- ::Foreman::Deprecation.api_deprecation_warning("Remote actions using katello-agent are deprecated and will be removed in Katello 4.0. " \
43
- "You may consider switching to Remote Execution.")
42
+ ::Foreman::Deprecation.api_deprecation_warning("Katello-agent is deprecated and will be removed in a future release.")
44
43
  end
45
44
 
46
45
  def full_result_response(collection)
@@ -73,7 +73,7 @@ module Katello
73
73
  respond :resource => @filter
74
74
  end
75
75
 
76
- api :delefind_filterte, "/content_views/:content_view_id/filters/:id", N_("delete a filter")
76
+ api :delete, "/content_views/:content_view_id/filters/:id", N_("delete a filter")
77
77
  api :delete, "/content_view_filters/:id", N_("delete a filter")
78
78
  param :content_view_id, :number, :desc => N_("content view identifier")
79
79
  param :id, :number, :desc => N_("filter identifier"), :required => true
@@ -39,7 +39,20 @@ module Katello
39
39
  end
40
40
 
41
41
  def throw_resource_not_found(name: resource_name, id: params[:id])
42
- fail HttpErrors::NotFound, _("Could not find %{name} resource with id %{id}") % {id: id, name: name}
42
+ perms_message = "Potential missing permissions: " +
43
+ missing_permissions.map(&:name).join(', ')
44
+ fail HttpErrors::NotFound, _("Could not find %{name} resource with id %{id}. %{perms_message}") % {id: id, name: name, perms_message: perms_message}
45
+ end
46
+
47
+ def missing_permissions
48
+ missing_perms = ::Foreman::AccessControl.permissions_for_controller_action(path_to_authenticate)
49
+
50
+ # promote_or_remove_content_views_to_environments has a special relationship to promote_or_remove_content_views
51
+ if path_to_authenticate["controller"] == "katello/api/v2/content_view_versions" &&
52
+ path_to_authenticate["action"].in?(["promote", "remove_from_environment", "remove", "republish_repositories"])
53
+ missing_perms << ::Permission.find_by(name: "promote_or_remove_content_views_to_environments")
54
+ end
55
+ missing_perms
43
56
  end
44
57
 
45
58
  def throw_resources_not_found(name:, expected_ids: [])
@@ -14,7 +14,7 @@ module Actions
14
14
  host.subscription_facet.purpose_addons = purpose_addon_objects
15
15
  end
16
16
 
17
- host.subscription_facet.save!
17
+ host.save!
18
18
  plan_self(:hostname => host.name)
19
19
  end
20
20
 
@@ -8,7 +8,7 @@ module Actions
8
8
  def plan(proxy)
9
9
  sequence do
10
10
  plan_action(Actions::Pulp::Orchestration::OrphanCleanup::RemoveOrphans, proxy)
11
- if proxy.pulp3_enabled?
11
+ if proxy.pulp3_enabled? && ::Katello::Ping.pulpcore_enabled
12
12
  plan_action(
13
13
  Actions::Pulp3::Orchestration::OrphanCleanup::RemoveOrphans,
14
14
  proxy)
@@ -72,7 +72,11 @@ module Actions
72
72
  end
73
73
  plan_self(:id => repo.id, :sync_result => output, :skip_metadata_check => skip_metadata_check, :validate_contents => validate_contents,
74
74
  :contents_changed => contents_changed)
75
- plan_action(Katello::Repository::ImportApplicability, :repo_id => repo.id, :contents_changed => contents_changed) if generate_applicability
75
+
76
+ if generate_applicability && !SETTINGS[:katello][:katello_applicability]
77
+ plan_action(Katello::Repository::ImportApplicability, :repo_id => repo.id, :contents_changed => contents_changed)
78
+ end
79
+
76
80
  plan_action(Katello::Repository::SyncHook, :id => repo.id)
77
81
  end
78
82
  end
@@ -0,0 +1,22 @@
1
+ module Actions
2
+ module Pulp3
3
+ class ContentMigrationReset < Pulp3::AbstractAsyncTask
4
+ def plan(smart_proxy)
5
+ plan_self(smart_proxy_id: smart_proxy.id)
6
+ end
7
+
8
+ def invoke_external_task
9
+ migration_service = ::Katello::Pulp3::Migration.new(smart_proxy)
10
+ migration_service.reset
11
+ end
12
+
13
+ def humanized_name
14
+ _("Content Migration Reset")
15
+ end
16
+
17
+ def rescue_strategy
18
+ Dynflow::Action::Rescue::Skip
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,126 @@
1
+ # Used exclusively by fix_hostgroup_facets.rake task
2
+ module Katello
3
+ module Util
4
+ class HostgroupFacetsHelper
5
+ def initialize
6
+ @logger = Logger.new($stdout)
7
+ end
8
+
9
+ def interested_hostgroups
10
+ groups = ::Hostgroup.unscoped.where(
11
+ id: Katello::Hostgroup::ContentFacet.
12
+ where(content_source_id: nil,
13
+ kickstart_repository_id: nil,
14
+ content_view_id: nil,
15
+ lifecycle_environment_id: nil).select(:hostgroup_id))
16
+ parents = groups.select { |group| group.parent.blank? }
17
+ children = groups.reject { |group| group.parent.blank? }
18
+ # we want the parents to get created before the children
19
+ # hence the order
20
+ parents + children
21
+ end
22
+
23
+ def pick_facet_values(hg)
24
+ # This call looks at the audit logs for a host group.
25
+ # Pries out information related to lce, ks, cv and content_source_id from the audit logs.
26
+ # The audit logs typically only contain updates.
27
+ # So if the user changed just the content_view_id, then that is the only thing marked as audited_changes.
28
+ # Hence we need to go through all the audit logs until we have information on lce, ks, cv and cs.
29
+ # If there was only one audit log and that was during the creation of hostgroup
30
+ # the audited changes look like this
31
+ # ```ruby
32
+ # {
33
+ # content_view_id: 10,
34
+ # kickstart_repository_id: 1000
35
+ # ......
36
+ # }
37
+ # ```
38
+ # However if you updated the hostgroup and set the kickstart_repository_id, or
39
+ # content_view_id then audited changes look like
40
+ # ```ruby
41
+ # {
42
+ # content_view_id: [10, 11],
43
+ # kickstart_repository_id: [1000, 1200]
44
+ # ......
45
+ # }
46
+ # ```
47
+ # So the code says "if the attribute value is an array pick the last value else just keep the value as it is "
48
+
49
+ # Further along it is to be noted that `hostgroup.audits` returns the audits ordered by the version number in ascending order, so the latest audit will be `hostgroup.audits.last`
50
+
51
+ # We want to iterate though each audit from latest audit to start, and as soon as we find a content_view_id key or kickstart_repository_id key or lifecycle environment_id key or content_source_id key we want it to be set once.
52
+
53
+ # So if I had an audit history like
54
+ # ``` ruby
55
+ # {
56
+ # content_view_id: 10,
57
+ # kickstart_repository_id: 1000,
58
+ # version:1
59
+ # ......
60
+ # },
61
+ # {
62
+ # content_view_id: [10, 11],
63
+ # kickstart_repository_id: [1000, 1200],
64
+ # version: 2
65
+ # ......
66
+ # }
67
+ # ```
68
+
69
+ # The code would start at version 2, notice that cv_id and ks_repo were set there
70
+ # and keep them as the final.
71
+ # So when it goes to version 1 since cv_id and ks_repo are already set,
72
+ # it will ignore. It will finally
73
+ # return {content_view_id: 11, kickstart_repository_id: 1200}
74
+ facet_values = {}
75
+ hg.audits.reverse_each do |audit|
76
+ hg_changes = audit.audited_changes.slice("lifecycle_environment_id",
77
+ "kickstart_repository_id",
78
+ "content_view_id",
79
+ "content_source_id")
80
+ facet_values = hg_changes.merge(facet_values)
81
+ end
82
+
83
+ values = facet_values.map do |k, v|
84
+ v = v[-1] if v.is_a? Array
85
+ [k, v]
86
+ end
87
+ values.to_h.with_indifferent_access
88
+ end
89
+
90
+ def main
91
+ bad_hgs = []
92
+ good_hgs = []
93
+
94
+ groups = interested_hostgroups.each do |hg|
95
+ facet = hg.content_facet
96
+ values = pick_facet_values(hg)
97
+ if !values.empty? && facet.update(values)
98
+ good_hgs << { hostgroup: hg, facet_values: values }
99
+ else
100
+ bad_hgs << { hostgroup: hg, facet_values: values }
101
+ facet.save(validate: false)
102
+ end
103
+ end
104
+
105
+ unless bad_hgs.empty?
106
+ @logger.warn "Some of the hostgroups reported a validation error. "\
107
+ "The hostgroups have been updated. "\
108
+ "Check via the Web UI."
109
+
110
+ bad_hgs.each do |bad_group|
111
+ @logger.warn "Hostgroup #{bad_group[:hostgroup]}"
112
+ @logger.warn "Facet Values #{bad_group[:facet_values]}"
113
+ end
114
+ end
115
+ unless good_hgs.empty?
116
+ @logger.info "Following hostgroups were succesfully updated."
117
+ good_hgs.each do |good_group|
118
+ @logger.info "Hostgroup #{good_group[:hostgroup]}"
119
+ @logger.info "Facet Values #{good_group[:facet_values]}"
120
+ end
121
+ end
122
+ @logger.info("#{groups.count} Hostgroup(s) were updated.")
123
+ end
124
+ end
125
+ end
126
+ end
@@ -8,7 +8,7 @@ module Katello
8
8
  def filter_package_groups_by_pulp_href(package_groups, package_pulp_hrefs)
9
9
  rpms = Katello::Rpm.where(:pulp_id => package_pulp_hrefs)
10
10
  package_groups.reject do |package_group|
11
- (package_group.package_names & rpms.pluck(:name)).empty?
11
+ (package_group.package_names - rpms.pluck(:name)).any?
12
12
  end
13
13
  end
14
14
 
@@ -107,7 +107,9 @@ module Katello
107
107
  return true unless operatingsystem
108
108
 
109
109
  if operatingsystem.respond_to? :kickstart_repos
110
- return operatingsystem.kickstart_repos(self).any? { |repo| repo[:id] == (content_facet&.kickstart_repository_id || content_facet&.kickstart_repository&.id) }
110
+ operatingsystem.kickstart_repos(self, content_facet: content_facet).any? do |repo|
111
+ repo[:id] == (content_facet&.kickstart_repository_id || content_facet&.kickstart_repository&.id)
112
+ end
111
113
  end
112
114
  end
113
115
 
@@ -126,7 +128,7 @@ module Katello
126
128
  facet_model = Facets.registered_facets[facet].hostgroup_configuration.model
127
129
  value = facet_model.where.not(attribute => nil).joins(:hostgroup).merge(
128
130
  ::Hostgroup.where(id: self.ancestor_ids).reorder(ancestry: :desc)
129
- ).limit(1).pluck(attribute)
131
+ ).limit(1).pluck(attribute).first
130
132
  end
131
133
  value
132
134
  end
@@ -218,6 +218,18 @@ module Katello
218
218
  def db_values(new_ids, pulp_id_href_map, repository)
219
219
  new_ids.map { |unit_id| [unit_id.to_i, pulp_id_href_map.dig(unit_id), repository.id.to_i, Time.now.utc.to_s(:db), Time.now.utc.to_s(:db)].compact }
220
220
  end
221
+
222
+ def unmigrated_content
223
+ self.where(migrated_pulp3_href: nil, ignore_missing_from_migration: false)
224
+ end
225
+
226
+ def missing_migrated_content #missing or corrupted content that could not be migrated
227
+ self.where(migrated_pulp3_href: nil, missing_from_migration: true, ignore_missing_from_migration: false)
228
+ end
229
+
230
+ def ignored_missing_migrated_content
231
+ self.where(migrated_pulp3_href: nil, missing_from_migration: true, ignore_missing_from_migration: true)
232
+ end
221
233
  end
222
234
  end
223
235
  end
@@ -42,10 +42,11 @@ module Katello
42
42
  end
43
43
  end
44
44
 
45
- def kickstart_repos(host)
46
- distros = distribution_repositories(host).where(distribution_bootable: true)
47
- if distros && host.content_source
48
- distros.map { |distro| distro.to_hash(host.content_source) }
45
+ def kickstart_repos(host, content_facet: nil)
46
+ distros = distribution_repositories(host, content_facet: content_facet).where(distribution_bootable: true)
47
+ content_facet ||= host.content_facet
48
+ if distros && content_facet&.content_source
49
+ distros.map { |distro| distro.to_hash(content_facet.content_source) }
49
50
  else
50
51
  []
51
52
  end
@@ -65,10 +66,10 @@ module Katello
65
66
  end
66
67
  end
67
68
 
68
- def distribution_repositories(host)
69
- content_view = host.try(:content_facet).try(:content_view) || host.try(:content_view)
70
- lifecycle_environment = host.try(:content_facet).try(:lifecycle_environment) || host.try(:lifecycle_environment)
71
-
69
+ def distribution_repositories(host, content_facet: nil)
70
+ content_facet ||= host.content_facet
71
+ content_view = content_facet.try(:content_view) || host.try(:content_view)
72
+ lifecycle_environment = content_facet.try(:lifecycle_environment) || host.try(:lifecycle_environment)
72
73
  if content_view && lifecycle_environment && host.os && host.architecture
73
74
  Katello::Repository.in_environment(lifecycle_environment).in_content_views([content_view]).
74
75
  where(:distribution_arch => host.architecture.name).
@@ -179,7 +179,9 @@ module Katello
179
179
  end
180
180
 
181
181
  def fix_pulp3_capabilities(type)
182
- if missing_pulp3_capabilities? && !pulp2_preferred_for_type?(type)
182
+ repository_type_obj = type.is_a?(String) || type.is_a?(Symbol) ? Katello::RepositoryTypeManager.repository_types[type] : type
183
+
184
+ if missing_pulp3_capabilities? && repository_type_obj.pulp3_plugin && !pulp2_preferred_for_type?(repository_type_obj.id)
183
185
  self.refresh
184
186
  if self.capabilities(::SmartProxy::PULP3_FEATURE).empty?
185
187
  fail Katello::Errors::PulpcoreMissingCapabilities
@@ -13,6 +13,10 @@ module Katello
13
13
  order(:name)
14
14
  end
15
15
 
16
+ def filename
17
+ path
18
+ end
19
+
16
20
  def self.total_for_repositories(repos)
17
21
  self.in_repositories(repos).count
18
22
  end