katello 4.0.0 → 4.0.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +2 -4
  3. data/app/controllers/katello/api/v2/repositories_controller.rb +3 -3
  4. data/app/lib/actions/katello/agent_action.rb +26 -17
  5. data/app/lib/actions/katello/bulk_agent_action.rb +18 -5
  6. data/app/lib/actions/katello/content_view/promote.rb +25 -0
  7. data/app/lib/actions/katello/content_view/publish.rb +29 -0
  8. data/app/lib/actions/katello/jail_concern/content_view.rb +30 -0
  9. data/app/lib/actions/katello/jail_concern/organization.rb +30 -0
  10. data/app/lib/actions/katello/repository/sync.rb +59 -0
  11. data/app/lib/katello/agent/client_message_handler.rb +12 -3
  12. data/app/lib/katello/event_daemon/monitor.rb +1 -0
  13. data/app/lib/katello/event_daemon/services/agent_event_receiver.rb +6 -10
  14. data/app/models/katello/agent/dispatch_history.rb +2 -0
  15. data/app/models/katello/concerns/hostgroup_extensions.rb +1 -1
  16. data/app/models/katello/concerns/redhat_extensions.rb +5 -8
  17. data/app/models/katello/content_view.rb +13 -7
  18. data/app/models/katello/ping.rb +1 -1
  19. data/app/models/katello/repository.rb +2 -2
  20. data/app/models/katello/root_repository.rb +1 -1
  21. data/app/services/katello/agent/dispatcher.rb +18 -24
  22. data/app/services/katello/applicability/applicable_content_helper.rb +8 -6
  23. data/app/services/katello/managed_content_medium_provider.rb +3 -3
  24. data/app/services/katello/pulp3/migration.rb +4 -3
  25. data/app/services/katello/pulp3/repository.rb +32 -4
  26. data/app/services/katello/pulp3/repository/apt.rb +1 -2
  27. data/app/services/katello/pulp3/repository/yum.rb +1 -1
  28. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +2 -2
  29. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +3 -4
  30. data/lib/katello/engine.rb +11 -10
  31. data/lib/katello/plugin.rb +5 -2
  32. data/lib/katello/tasks/clean_backend_objects.rake +0 -25
  33. data/lib/katello/version.rb +1 -1
  34. metadata +13 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a68ac9c26daead6a548c835d225d8dbebe0ce33ca5b6315b6505ef64660d9ee6
4
- data.tar.gz: '09d66a70cef13109aff1e38a9e409862c5cae6263c5c7aea415399c7cd0e08b0'
3
+ metadata.gz: 02ff428534ded98d248efb23e9684364f8930cb1175c25a70ad3529963e435a0
4
+ data.tar.gz: 680e66df3f38df78a5feface53e92cc4129cc4a847fbd5e344ab63e5dd6bfd40
5
5
  SHA512:
6
- metadata.gz: cd9715ea64ef37bf05df3ae49a209e15f0ffe0c51d8f69d4b3fcba119909349fea1fe1ab4f464240515057e1529304850176ec5a48cbbfb25cd04fb1ee788137
7
- data.tar.gz: 6ab0c5c117c51017b37ca1366fc05f64efcc7a17375e2bb4ac9ee3633990baedf68ba505cc29292295f5a01e31ac6fd4bef209d9272e2f5cfce0f3f9c7f29a65
6
+ metadata.gz: 32ccbc77851e99ff2e16ebb36219871a0e086f86f60da1d3265e26cddbfdb7563c1363fc72ba5d3db34fb8360eb01ddb678fc882a41a16e635397631ab6b767e
7
+ data.tar.gz: 882219885a8aa1dd105a6b24a630092984d462fcac3a2030fa8fb3e6c112225ac48b00e746f7194bbb4e5ff2fb1d8766610b65434e0555958b4b251668be4cf6
@@ -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']
@@ -52,9 +52,9 @@ module Katello
52
52
  param :upstream_password, String, :desc => N_("Password of the upstream repository user used for authentication")
53
53
  param :ostree_upstream_sync_policy, ::Katello::RootRepository::OSTREE_UPSTREAM_SYNC_POLICIES, :desc => N_("policies for syncing upstream ostree repositories")
54
54
  param :ostree_upstream_sync_depth, :number, :desc => N_("if a custom sync policy is chosen for ostree repositories then a 'depth' value must be provided")
55
- param :deb_releases, String, :desc => N_("comma-separated list of releases to be synced from deb-archive")
56
- param :deb_components, String, :desc => N_("comma-separated list of repo components to be synced from deb-archive")
57
- param :deb_architectures, String, :desc => N_("comma-separated list of architectures to be synced from deb-archive")
55
+ param :deb_releases, String, :desc => N_("whitespace-separated list of releases to be synced from deb-archive")
56
+ param :deb_components, String, :desc => N_("whitespace-separated list of repo components to be synced from deb-archive")
57
+ param :deb_architectures, String, :desc => N_("whitespace-separated list of architectures to be synced from deb-archive")
58
58
  param :ignorable_content, Array, :desc => N_("List of content units to ignore while syncing a yum repository. Must be subset of %s") % RootRepository::IGNORABLE_CONTENT_UNIT_TYPES.join(",")
59
59
  param :ansible_collection_requirements, String, :desc => N_("Contents of requirement yaml file to sync from URL")
60
60
  param :http_proxy_policy, ::Katello::RootRepository::HTTP_PROXY_POLICIES, :desc => N_("policies for HTTP proxy for content sync")
@@ -15,22 +15,15 @@ module Actions
15
15
  def plan(host, options)
16
16
  action_subject(host, :hostname => host.name, :content => options[:content])
17
17
 
18
- # if already dispatched by bulk action use the provided history ID
19
- dispatch_history_id = options.dig(:dispatch_histories, host.id.to_s)
20
-
21
- unless dispatch_history_id
22
- histories = ::Katello::Agent::Dispatcher.dispatch(
23
- self.class.agent_message,
24
- [host.id],
25
- content: options[:content]
26
- )
27
-
28
- dispatch_history_id = histories.first.id
29
- end
18
+ dispatch_history_id = options.dig(:dispatch_histories, host.id.to_s) || ::Katello::Agent::Dispatcher.create_histories(
19
+ host_ids: [host.id]
20
+ ).first.id
30
21
 
31
22
  plan_self(
32
23
  host_id: host.id,
33
- dispatch_history_id: dispatch_history_id
24
+ dispatch_history_id: dispatch_history_id,
25
+ content: options[:content],
26
+ bulk: options[:bulk]
34
27
  )
35
28
  end
36
29
 
@@ -38,20 +31,23 @@ module Actions
38
31
  case event
39
32
  when nil
40
33
  history = dispatch_history
34
+ timeout = accept_timeout
41
35
 
42
36
  if history.finished?
43
37
  fail_on_errors
44
38
  return
45
39
  elsif history.accepted?
46
- schedule_timeout(finish_timeout)
47
- else
48
- schedule_timeout(accept_timeout)
40
+ timeout = finish_timeout
49
41
  end
50
42
 
51
43
  suspend do |suspended_action|
52
44
  history.dynflow_execution_plan_id = suspended_action.execution_plan_id
53
45
  history.dynflow_step_id = suspended_action.step_id
54
46
  history.save!
47
+
48
+ dispatch_message(history) unless input[:bulk]
49
+
50
+ schedule_timeout(timeout)
55
51
  end
56
52
  when Dynflow::Action::Timeouts::Timeout
57
53
  process_timeout
@@ -63,6 +59,14 @@ module Actions
63
59
  end
64
60
  end
65
61
 
62
+ def dispatch_message(history)
63
+ ::Katello::Agent::Dispatcher.dispatch(
64
+ self.class.agent_message,
65
+ [history],
66
+ content: input[:content]
67
+ )
68
+ end
69
+
66
70
  def accept_timeout
67
71
  Setting['content_action_accept_timeout']
68
72
  end
@@ -79,7 +83,12 @@ module Actions
79
83
  end
80
84
 
81
85
  unless history.finished?
82
- fail _("Host did not finish content action in %s seconds. The task has been cancelled.") % finish_timeout
86
+ # we could be processing the accept_timeout here
87
+ # only fail for finish_timeout unless the actual duration has elapsed
88
+ finish_limit = history.accepted_at + finish_timeout
89
+ if finish_limit < DateTime.now
90
+ fail _("Host did not finish content action in %s seconds. The task has been cancelled.") % finish_timeout
91
+ end
83
92
  end
84
93
  end
85
94
 
@@ -3,19 +3,32 @@ module Actions
3
3
  class BulkAgentAction < Actions::BulkAction
4
4
  def plan(agent_action, hosts, args)
5
5
  host_ids = hosts.map(&:id)
6
- dispatch_args = {
7
- content: args[:content]
8
- }
9
- histories = ::Katello::Agent::Dispatcher.dispatch(agent_action.agent_message, host_ids, dispatch_args)
6
+
7
+ histories = ::Katello::Agent::Dispatcher.create_histories(
8
+ host_ids: host_ids
9
+ )
10
10
 
11
11
  grouped_histories = {}
12
12
  histories.each { |h| grouped_histories[h.host_id] = h.id }
13
13
  options = {
14
14
  dispatch_histories: grouped_histories,
15
- content: args[:content]
15
+ type: agent_action.agent_message,
16
+ content: args[:content],
17
+ bulk: true
16
18
  }
17
19
  super(agent_action, hosts, options)
18
20
  end
21
+
22
+ def spawn_plans
23
+ args = input[:args].first
24
+ histories = ::Katello::Agent::DispatchHistory.where(id: args[:dispatch_histories].slice(*current_batch.map(&:to_s)).values)
25
+ ::Katello::Agent::Dispatcher.dispatch(
26
+ args[:type].to_sym,
27
+ histories,
28
+ content: args[:content]
29
+ )
30
+ super
31
+ end
19
32
  end
20
33
  end
21
34
  end
@@ -2,6 +2,9 @@ module Actions
2
2
  module Katello
3
3
  module ContentView
4
4
  class Promote < Actions::EntryAction
5
+ extend ApipieDSL::Class
6
+ include ::Actions::ObservableAction
7
+
5
8
  def plan(version, environments, is_force = false, description = nil, incremental_update = false)
6
9
  action_subject(version.content_view)
7
10
  version.check_ready_to_promote!(environments)
@@ -18,6 +21,28 @@ module Actions
18
21
  end
19
22
  end
20
23
  end
24
+
25
+ def environments
26
+ input['environments']
27
+ end
28
+
29
+ apipie :class, "A class representing #{self} object" do
30
+ desc 'This object is available as **@object** variable in
31
+ webhook templates when a corresponding event occures.
32
+ The following properties can be used to retrieve the needed information.'
33
+ name "#{class_scope}"
34
+ refs "#{class_scope}"
35
+ sections only: %w[all webhooks]
36
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
37
+ property :environments, array_of: String, desc: 'Returns the list of environments the content view was promoted to'
38
+ end
39
+ include Actions::Katello::JailConcern::Organization
40
+ include Actions::Katello::JailConcern::ContentView
41
+ class Jail < ::Actions::ObservableAction::Jail
42
+ allow :organization_id, :organization_name, :organization_label,
43
+ :content_view_id, :content_view_name, :content_view_label,
44
+ :environments
45
+ end
21
46
  end
22
47
  end
23
48
  end
@@ -3,7 +3,9 @@ module Actions
3
3
  module Katello
4
4
  module ContentView
5
5
  class Publish < Actions::EntryAction
6
+ extend ApipieDSL::Class
6
7
  include ::Katello::ContentViewHelper
8
+ include ::Actions::ObservableAction
7
9
  attr_accessor :version
8
10
  # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity
9
11
  def plan(content_view, description = "", options = {importing: false}) # rubocop:disable Metrics/PerceivedComplexity
@@ -145,6 +147,14 @@ module Actions
145
147
  rescue ::Katello::Errors::CapsuleCannotBeReached # skip any capsules that cannot be connected to
146
148
  end
147
149
 
150
+ def content_view_version_id
151
+ input['content_view_version_id']
152
+ end
153
+
154
+ def content_view_version_name
155
+ input['content_view_version_name']
156
+ end
157
+
148
158
  private
149
159
 
150
160
  def include_other_components(override_components, content_view)
@@ -197,6 +207,25 @@ module Actions
197
207
  end
198
208
  end
199
209
  end
210
+
211
+ apipie :class, "A class representing #{self} object" do
212
+ desc 'This object is available as **@object** variable in
213
+ webhook templates when a corresponding event occures.
214
+ The following properties can be used to retrieve the needed information.'
215
+ name "#{class_scope}"
216
+ refs "#{class_scope}"
217
+ sections only: %w[all webhooks]
218
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
219
+ property :content_view_version_id, Integer, desc: 'Returns published content view version id'
220
+ property :content_view_version_name, String, desc: 'Returns published content view version name'
221
+ end
222
+ include Actions::Katello::JailConcern::Organization
223
+ include Actions::Katello::JailConcern::ContentView
224
+ class Jail < ::Actions::ObservableAction::Jail
225
+ allow :organization_id, :organization_name, :organization_label,
226
+ :content_view_id, :content_view_name, :content_view_label,
227
+ :content_view_version_id, :content_view_version_name
228
+ end
200
229
  end
201
230
  end
202
231
  end
@@ -0,0 +1,30 @@
1
+ module Actions
2
+ module Katello
3
+ module JailConcern
4
+ module ContentView
5
+ def content_view_id
6
+ input['content_view']['id']
7
+ end
8
+
9
+ def content_view_name
10
+ input['content_view']['name']
11
+ end
12
+
13
+ def content_view_label
14
+ input['content_view']['label']
15
+ end
16
+
17
+ def self.included(base)
18
+ super
19
+ base.instance_eval do
20
+ apipie :class do
21
+ property :content_view_id, Integer, desc: 'Returns the id of the content view'
22
+ property :content_view_name, String, desc: 'Returns the name of the content view'
23
+ property :content_view_label, String, desc: 'Returns the label of the content view'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Actions
2
+ module Katello
3
+ module JailConcern
4
+ module Organization
5
+ def organization_id
6
+ input['organization']['id']
7
+ end
8
+
9
+ def organization_name
10
+ input['organization']['name']
11
+ end
12
+
13
+ def organization_label
14
+ input['organization']['label']
15
+ end
16
+
17
+ def self.included(base)
18
+ super
19
+ base.instance_eval do
20
+ apipie :class do
21
+ property :organization_id, Integer, desc: 'Returns the id of the organization'
22
+ property :organization_name, String, desc: 'Returns the name of the organization'
23
+ property :organization_label, String, desc: 'Returns the label of the organization'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -3,8 +3,10 @@ module Actions
3
3
  module Katello
4
4
  module Repository
5
5
  class Sync < Actions::EntryAction
6
+ extend ApipieDSL::Class
6
7
  include Helpers::Presenter
7
8
  include Actions::Katello::PulpSelector
9
+ include ::Actions::ObservableAction
8
10
  middleware.use Actions::Middleware::ExecuteIfContentsChanged
9
11
 
10
12
  input_format do
@@ -124,6 +126,63 @@ module Actions
124
126
  def rescue_strategy
125
127
  Dynflow::Action::Rescue::Skip
126
128
  end
129
+
130
+ def repository_id
131
+ input['repository']['id']
132
+ end
133
+
134
+ def repository_name
135
+ input['repository']['name']
136
+ end
137
+
138
+ def repository_label
139
+ input['repository']['label']
140
+ end
141
+
142
+ def product_id
143
+ input['product']['id']
144
+ end
145
+
146
+ def product_name
147
+ input['product']['name']
148
+ end
149
+
150
+ def product_label
151
+ input['product']['label']
152
+ end
153
+
154
+ def contents_changed
155
+ input['contents_changed']
156
+ end
157
+
158
+ def sync_result
159
+ input['sync_result']
160
+ end
161
+
162
+ apipie :class, "A class representing #{self} object" do
163
+ desc 'This object is available as **@object** variable in
164
+ webhook templates when a corresponding event occures.
165
+ The following properties can be used to retrieve the needed information.'
166
+ name "#{class_scope}"
167
+ refs "#{class_scope}"
168
+ sections only: %w[all webhooks]
169
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
170
+ property :repository_id, Integer, desc: 'Returns synced repository id'
171
+ property :repository_name, String, desc: 'Returns synced repository name'
172
+ property :repository_label, String, desc: 'Returns synced repository label'
173
+ property :product_id, Integer, desc: 'Returns product id the synced repository belongs to'
174
+ property :product_name, String, desc: 'Returns product name the synced repository belongs to'
175
+ property :product_label, String, desc: 'Returns product label the synced repository belongs to'
176
+ property :sync_result, Hash, desc: 'Returns Hash object with sync result'
177
+ property :contents_changed, one_of: [true, false], desc: 'Returns true if repository content was changed due to sync, false otherwise'
178
+ end
179
+ include Actions::Katello::JailConcern::Organization
180
+ class Jail < ::Actions::ObservableAction::Jail
181
+ allow :organization_id, :organization_name, :organization_label,
182
+ :repository_id, :repository_name, :repository_label,
183
+ :product_id, :product_name, :product_label,
184
+ :sync_result, :contents_changed
185
+ end
127
186
  end
128
187
  end
129
188
  end
@@ -8,6 +8,7 @@ module Katello
8
8
  @dispatch_history = Katello::Agent::DispatchHistory.find_by_id(dispatch_history_id)
9
9
 
10
10
  unless @dispatch_history
11
+ logger.error("Invalid client message: #{@json}")
11
12
  fail("No valid dispatch history in client message")
12
13
  end
13
14
  end
@@ -33,9 +34,17 @@ module Katello
33
34
  private
34
35
 
35
36
  def handle_dynflow_event
36
- task_exists = ForemanTasks::Task.exists?(external_id: @dispatch_history.dynflow_execution_plan_id, result: 'pending')
37
- unless task_exists
38
- logger.warn("Couldn't find pending task with external_id=#{@dispatch_history.dynflow_execution_plan_id} dispatch_history_id=#{@dispatch_history.id}")
37
+ return unless accepted? || result
38
+
39
+ task = ForemanTasks::Task.find_by_external_id(@dispatch_history.dynflow_execution_plan_id)
40
+
41
+ unless task
42
+ logger.info("Task external_id=#{@dispatch_history.dynflow_execution_plan_id} wasn't found. Skipping dynflow event dispatch")
43
+ return
44
+ end
45
+
46
+ unless task.result == 'pending'
47
+ logger.info("Task is no longer pending. Skipping dynflow event dispatch. task_result=#{task.result} external_id=#{@dispatch_history.dynflow_execution_plan_id} dispatch_history_id=#{@dispatch_history.id}")
39
48
  return
40
49
  end
41
50
 
@@ -38,6 +38,7 @@ module Katello
38
38
  begin
39
39
  service_class.close
40
40
  service_class.run
41
+ sleep 0.1
41
42
  @service_statuses[service_name] = service_class.status
42
43
  rescue => error
43
44
  Rails.logger.error("Error occurred while starting #{service_class}")
@@ -2,8 +2,6 @@ module Katello
2
2
  module EventDaemon
3
3
  module Services
4
4
  class AgentEventReceiver
5
- STATUS_CACHE_KEY = 'katello_agent_events'.freeze
6
-
7
5
  class Handler
8
6
  attr_accessor :processed, :failed
9
7
 
@@ -48,14 +46,12 @@ module Katello
48
46
  @agent_connection&.open? && @thread&.status.present?
49
47
  end
50
48
 
51
- def self.status(refresh: true)
52
- Rails.cache.fetch(STATUS_CACHE_KEY, force: refresh) do
53
- {
54
- running: running?,
55
- processed_count: @handler&.processed || 0,
56
- failed_count: @handler&.failed || 0
57
- }
58
- end
49
+ def self.status
50
+ {
51
+ running: running?,
52
+ processed_count: @handler&.processed || 0,
53
+ failed_count: @handler&.failed || 0
54
+ }
59
55
  end
60
56
  end
61
57
  end
@@ -3,6 +3,8 @@ module Katello
3
3
  class DispatchHistory < Katello::Model
4
4
  self.table_name = 'katello_agent_dispatch_histories'
5
5
 
6
+ belongs_to :host, :class_name => "::Host::Managed"
7
+
6
8
  serialize :result, Hash
7
9
 
8
10
  def accepted?
@@ -126,7 +126,7 @@ module Katello
126
126
  facet_model = Facets.registered_facets[facet].hostgroup_configuration.model
127
127
  value = facet_model.where.not(attribute => nil).joins(:hostgroup).merge(
128
128
  ::Hostgroup.where(id: self.ancestor_ids).reorder(ancestry: :desc)
129
- ).limit(1).pluck(attribute)
129
+ ).limit(1).pluck(attribute).first
130
130
  end
131
131
  value
132
132
  end
@@ -51,17 +51,14 @@ module Katello
51
51
  end
52
52
  end
53
53
 
54
- def variant_repo(host, variant)
54
+ def variant_repos(host, variant)
55
55
  if variant && host.content_source
56
56
  product_id = host.try(:content_facet).try(:kickstart_repository).try(:product_id) || host.try(:kickstart_repository).try(:product_id)
57
- distro = distribution_repositories(host)
57
+ distros = distribution_repositories(host)
58
58
  .joins(:product)
59
- .where(
60
- distribution_variant: variant,
61
- "#{Katello::Product.table_name}.id": product_id
62
- ).first
63
-
64
- distro&.to_hash(host.content_source)
59
+ .where("#{Katello::Repository.table_name}.distribution_variant LIKE :variant", { variant: "%#{variant}%" })
60
+ .where("#{Katello::Product.table_name}.id": product_id).collect { |repo| repo.to_hash(host.content_source, true) }
61
+ distros
65
62
  end
66
63
  end
67
64
 
@@ -6,6 +6,7 @@ module Katello
6
6
  include Ext::LabelFromName
7
7
  include Katello::Authorization::ContentView
8
8
  include ForemanTasks::Concerns::ActionSubject
9
+ include Foreman::ObservableModel
9
10
 
10
11
  CONTENT_DIR = "content_views".freeze
11
12
  IMPORT_LIBRARY = "Import-Library".freeze
@@ -90,6 +91,17 @@ module Katello
90
91
  scoped_search :on => :label, :complete_value => true
91
92
  scoped_search :on => :composite, :complete_value => true
92
93
 
94
+ set_crud_hooks :content_view
95
+
96
+ apipie :class, desc: "A class representing #{model_name.human} object" do
97
+ name 'Content View'
98
+ refs 'ContentView'
99
+ sections only: %w[all additional]
100
+ prop_group :katello_idname_props, Katello::Model, meta: { friendly_name: 'Content View' }
101
+ property :label, String, desc: 'Returns label of the Content View'
102
+ property :organization, 'Organization', desc: 'Returns organization object'
103
+ end
104
+
93
105
  def self.in_environment(env)
94
106
  joins(:content_view_environments).
95
107
  where("#{Katello::ContentViewEnvironment.table_name}.environment_id = ?", env.id)
@@ -757,14 +769,8 @@ module Katello
757
769
  self.organization
758
770
  end
759
771
 
760
- apipie :class, desc: "A class representing #{model_name.human} object" do
761
- name 'Content View'
762
- refs 'ContentView'
763
- sections only: %w[all additional]
764
- prop_group :katello_basic_props, Katello::Model, meta: { friendly_name: 'Content View' }
765
- end
766
772
  class Jail < ::Safemode::Jail
767
- allow :name, :label, :version
773
+ allow :id, :name, :label, :version, :organization
768
774
  end
769
775
  end
770
776
  end
@@ -72,7 +72,7 @@ module Katello
72
72
 
73
73
  def ping_katello_agent(result)
74
74
  exception_watch(result) do
75
- status = Katello::EventDaemon::Services::AgentEventReceiver.status(refresh: false)
75
+ status = Katello::EventDaemon::Runner.service_status(:katello_agent_events)
76
76
  event_daemon_status(status, result)
77
77
  end
78
78
  end
@@ -395,8 +395,8 @@ module Katello
395
395
  all_instances
396
396
  end
397
397
 
398
- def to_hash(content_source = nil)
399
- {id: id, name: label, url: full_path(content_source)}
398
+ def to_hash(content_source = nil, force_http = false)
399
+ {id: id, name: label, url: full_path(content_source, force_http)}
400
400
  end
401
401
 
402
402
  #is the repo cloned in the specified environment
@@ -139,7 +139,7 @@ module Katello
139
139
  def ensure_compatible_download_policy
140
140
  if !url.blank? && URI(url).scheme == 'file' &&
141
141
  [::Runcible::Models::YumImporter::DOWNLOAD_ON_DEMAND, ::Runcible::Models::YumImporter::DOWNLOAD_BACKGROUND].include?(download_policy)
142
- errors.add(:download_policy, _("Cannot sync file:// repositories with On Demand or Background Download Policies"))
142
+ errors.add(:download_policy, _("Cannot sync file:// repositories with the On Demand Download Policy"))
143
143
  end
144
144
  end
145
145
 
@@ -14,37 +14,31 @@ module Katello
14
14
  register_message(:install_package_group, Katello::Agent::InstallPackageGroupMessage)
15
15
  register_message(:remove_package_group, Katello::Agent::RemovePackageGroupMessage)
16
16
 
17
- def self.dispatch(message_type, host_ids, args)
17
+ def self.dispatch(message_type, histories, args)
18
18
  message_class = @supported_messages[message_type]
19
-
20
- fail("Unsupported message type") unless message_class
21
-
22
- uuid_data = ::Katello::Host::ContentFacet.where(host_id: host_ids).pluck(:host_id, :uuid)
23
- fail("Couldn't find all hosts specified") unless host_ids.size == uuid_data.size
24
-
25
- host_data = uuid_data.map do |host_id, consumer_id|
26
- {
27
- host_id: host_id,
28
- consumer_id: consumer_id,
29
- history: Katello::Agent::DispatchHistory.new(host_id: host_id),
30
- message: message_class.new(**args.merge(consumer_id: consumer_id))
31
- }
19
+ fail("Unsupported message type: #{message_type}") unless message_class
20
+
21
+ messages = histories.map do |history|
22
+ message = message_class.new(**args.merge(consumer_id: history.host.subscription_facet.uuid))
23
+ message.dispatch_history_id = history.id
24
+ message.recipient_address = settings[:client_queue_format] % history.host.subscription_facet.uuid
25
+ message.reply_to = settings[:event_queue_name]
26
+ message
32
27
  end
33
28
 
34
- histories = host_data.map { |attrs| attrs[:history] }
35
- ActiveRecord::Base.transaction do
36
- Katello::Agent::DispatchHistory.import(histories)
29
+ connection = Connection.new
30
+ connection.send_messages(messages)
37
31
 
38
- host_data.each do |d|
39
- d[:message].dispatch_history_id = d[:history].id
40
- d[:message].recipient_address = settings[:client_queue_format] % [d[:consumer_id]]
41
- d[:message].reply_to = settings[:event_queue_name]
42
- end
32
+ histories
33
+ end
43
34
 
44
- connection = Connection.new
45
- connection.send_messages(host_data.map { |d| d[:message] })
35
+ def self.create_histories(host_ids:)
36
+ histories = host_ids.map do |id|
37
+ Katello::Agent::DispatchHistory.new(host_id: id)
46
38
  end
47
39
 
40
+ Katello::Agent::DispatchHistory.import(histories)
41
+
48
42
  histories
49
43
  end
50
44
 
@@ -122,20 +122,22 @@ module Katello
122
122
  end
123
123
 
124
124
  def applicable_differences
125
- consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id")
126
- 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
127
128
 
128
- to_remove = consumer_ids - content_ids
129
- to_add = content_ids - consumer_ids
129
+ to_remove = consumer_ids - content_ids
130
+ to_add = content_ids - consumer_ids
130
131
 
131
- [to_add, to_remove]
132
+ [to_add, to_remove]
133
+ end
132
134
  end
133
135
 
134
136
  def insert(applicable_ids)
135
137
  unless applicable_ids.empty?
136
138
  inserts = applicable_ids.map { |applicable_id| "(#{applicable_id.to_i}, #{content_facet.id.to_i})" }
137
139
  sql = "INSERT INTO #{content_facet_association_class.table_name} (#{content_unit_association_id}, content_facet_id) VALUES #{inserts.join(', ')}"
138
- ActiveRecord::Base.connection.execute(sql)
140
+ ActiveRecord::Base.connection.exec_insert(sql)
139
141
  end
140
142
  end
141
143
 
@@ -14,11 +14,11 @@ module Katello
14
14
  URI.parse(url)
15
15
  end
16
16
 
17
- # If there is an 'AppStream' variant, we need to make it
17
+ # If there are any 'AppStream' variants, we need to make them
18
18
  # available to Anaconda
19
19
  def additional_media
20
- appstream = entity.operatingsystem.variant_repo(entity, 'AppStream')
21
- super + (appstream ? [appstream] : [])
20
+ appstream_repos = entity.operatingsystem.variant_repos(entity, 'AppStream')
21
+ super + (appstream_repos.present? ? appstream_repos : [])
22
22
  end
23
23
 
24
24
  def unique_id
@@ -293,10 +293,11 @@ module Katello
293
293
  to_import[[errata_id, repo_id]] ||= {erratum_id: errata_id, erratum_pulp3_href: pulp3_href, repository_id: repo_id}
294
294
  end
295
295
  end
296
- end
297
296
 
298
- Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
299
- on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
297
+ Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
298
+ on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
299
+ to_import = {}
300
+ end
300
301
  end
301
302
 
302
303
  def import_content_type(content_type)
@@ -222,11 +222,38 @@ module Katello
222
222
  end
223
223
 
224
224
  def refresh_distributions
225
- dist_ref = distribution_reference
226
- if dist_ref
227
- update_distribution
225
+ if repo.docker?
226
+ dist = lookup_distributions(base_path: repo.container_repository_name).first
228
227
  else
228
+ dist = lookup_distributions(base_path: repo.relative_path).first
229
+ end
230
+
231
+ # First check if the distribution exists
232
+ if dist
233
+ dist_ref = distribution_reference
234
+ # If we have a DistributionReference, update the distribution
235
+ if dist_ref
236
+ return update_distribution
237
+ # If no DistributionReference, create a DistributionReference and return
238
+ else
239
+ save_distribution_references([dist.pulp_href])
240
+ return []
241
+ end
242
+ end
243
+
244
+ # So far, it looks like there is no distribution. Try to create one.
245
+ begin
229
246
  create_distribution(relative_path)
247
+ rescue api.class.client_module::ApiError => e
248
+ # Now it seems there is a distribution. Fetch it and save the reference.
249
+ if e.message.include?("\"base_path\":[\"This field must be unique.\"]") ||
250
+ e.message.include?("\"base_path\":[\"Overlaps with existing distribution\"")
251
+ dist = lookup_distributions(base_path: repo.relative_path).first
252
+ save_distribution_references([dist.pulp_href])
253
+ return []
254
+ else
255
+ raise e
256
+ end
230
257
  end
231
258
  end
232
259
 
@@ -276,7 +303,8 @@ module Katello
276
303
  pulp3_distribution_data = api.get_distribution(href)
277
304
  path, content_guard_href = pulp3_distribution_data&.base_path, pulp3_distribution_data&.content_guard
278
305
  unless distribution_reference
279
- DistributionReference.create!(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href)
306
+ # Ensure that duplicates won't be created in the case of a race condition
307
+ DistributionReference.where(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href).first_or_create!
280
308
  end
281
309
  end
282
310
  end
@@ -27,8 +27,7 @@ module Katello
27
27
  popts = super(repository_version)
28
28
  popts.merge!(
29
29
  {
30
- # structured is not necessary for subscription-manager
31
- #structured: true, # publish real suites (e.g. 'stable')
30
+ structured: true, # publish real suites (e.g. 'stable')
32
31
  simple: true # publish all into 'default'-suite
33
32
  }
34
33
  )
@@ -61,7 +61,7 @@ module Katello
61
61
  )
62
62
  unless distribution.results.first.variants.empty?
63
63
  unless distribution.results.first.variants.first.name.nil?
64
- repo.update!(:distribution_variant => distribution.results.first.variants.first.name)
64
+ repo.update!(:distribution_variant => distribution.results.first.variants.map(&:name).join(','))
65
65
  end
66
66
  end
67
67
  end
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div class="help-block">
14
14
  <span translate>
15
- Selecting "Complete Sync" will cause only Yum repositories of the selected product to be synced. Selecting "Validate Content" will cause only Yum repositories using the "Immediate" or "Background" download policies will be synced.
15
+ Selecting "Complete Sync" will cause only Yum repositories of the selected product to be synced. Selecting "Validate Content" will cause only Yum repositories using the "Immediate" download policy to be synced.
16
16
  </span>
17
17
  </div>
18
18
 
@@ -23,4 +23,4 @@
23
23
  <span translate>Sync</span>
24
24
  </button>
25
25
  </span>
26
- </div>
26
+ </div>
@@ -108,7 +108,7 @@
108
108
  ng-model="repository.deb_releases"
109
109
  type="text"/>
110
110
  <p class="help-block" translate>
111
- Comma separated list of releases (suite or codename) to sync from. Default: stable
111
+ Whitespace-separated list of releases (suite or codename) to sync from. Default: stable
112
112
  </p>
113
113
  </div>
114
114
  <div ng-show="repository.content_type === 'deb'" bst-form-group label="{{ 'Components' | translate }}">
@@ -117,7 +117,7 @@
117
117
  ng-model="repository.deb_components"
118
118
  type="text"/>
119
119
  <p class="help-block" translate>
120
- Comma separated list of components to sync from (leave clear to sync all). Example: main
120
+ Whitespace-separated list of components to sync from (leave clear to sync all). Example: main
121
121
  </p>
122
122
  </div>
123
123
  <div ng-show="repository.content_type === 'deb'" bst-form-group label="{{ 'Architectures' | translate }}">
@@ -126,7 +126,7 @@
126
126
  ng-model="repository.deb_architectures"
127
127
  type="text"/>
128
128
  <p class="help-block" translate>
129
- Comma separated list of processor architectures to filter the sync by. Example: amd64
129
+ Whitespace-separated list of processor architectures to filter the sync by. Example: amd64
130
130
  </p>
131
131
  </div>
132
132
 
@@ -216,7 +216,6 @@
216
216
  <p class="help-block" translate>
217
217
  For On Demand synchronization, only the metadata is downloaded during sync and packages are fetched and stored on the filesystem when clients request them.
218
218
  On Demand is not recommended for custom repositories unless the upstream repository maintains older versions of packages within the repository.
219
- For Background synchronization, a background task will download all packages after the initial sync (Deprecated).
220
219
  The Immediate option will download all metadata and packages immediately during the sync.
221
220
  </p>
222
221
  </div>
@@ -79,16 +79,6 @@ module Katello
79
79
  require 'katello/apipie/validators'
80
80
  end
81
81
 
82
- # make sure the Katello plugin is initialized before `after_initialize`
83
- # hook so that the resumed Dynflow tasks can rely on everything ready.
84
- initializer 'katello.register_plugin', :before => :finisher_hook, :after => 'foreman_remote_execution.register_plugin' do
85
- ::Foreman::AccessControl::Permission.prepend ::Katello::Concerns::PermissionExtensions
86
- require 'katello/plugin'
87
-
88
- # extend builtin permissions from core with new actions
89
- require 'katello/permissions'
90
- end
91
-
92
82
  initializer "katello.register_actions", :before => :finisher_hook do |_app|
93
83
  ForemanTasks.dynflow.require!
94
84
  if (Setting.table_exists? rescue(false)) && Setting['host_tasks_workers_pool_size'].to_i > 0
@@ -99,6 +89,17 @@ module Katello
99
89
  #{Katello::Engine.root}/app/lib/headpin/actions
100
90
  #{Katello::Engine.root}/app/lib/katello/actions)
101
91
  ForemanTasks.dynflow.config.eager_load_paths.concat(action_paths)
92
+ ForemanTasks.dynflow.eager_load_actions!
93
+ end
94
+
95
+ # make sure the Katello plugin is initialized before `after_initialize`
96
+ # hook so that the resumed Dynflow tasks can rely on everything ready.
97
+ initializer 'katello.register_plugin', :before => :finisher_hook, :after => 'foreman_remote_execution.register_plugin' do
98
+ ::Foreman::AccessControl::Permission.prepend ::Katello::Concerns::PermissionExtensions
99
+ require 'katello/plugin'
100
+
101
+ # extend builtin permissions from core with new actions
102
+ require 'katello/permissions'
102
103
  end
103
104
 
104
105
  initializer "katello.set_dynflow_middlewares", :before => :finisher_hook do |_app|
@@ -3,7 +3,7 @@ require 'katello/repository_types.rb'
3
3
  require 'katello/host_status_manager.rb'
4
4
  # rubocop:disable Metrics/BlockLength
5
5
  Foreman::Plugin.register :katello do
6
- requires_foreman '>= 2.3'
6
+ requires_foreman '>= 2.4'
7
7
 
8
8
  sub_menu :top_menu, :content_menu, :caption => N_('Content'),
9
9
  :icon => 'fa fa-book', :after => :monitor_menu do
@@ -229,7 +229,8 @@ Foreman::Plugin.register :katello do
229
229
  apipie_documented_controllers ["#{Katello::Engine.root}/app/controllers/katello/api/v2/*.rb"]
230
230
  apipie_ignored_controllers %w(::Api::V2::OrganizationsController)
231
231
  ApipieDSL.configuration.dsl_classes_matchers.concat [
232
- "#{Katello::Engine.root}/app/models/katello/**/*.rb"
232
+ "#{Katello::Engine.root}/app/models/katello/**/*.rb",
233
+ "#{Katello::Engine.root}/app/lib/actions/**/*.rb"
233
234
  ]
234
235
 
235
236
  parameter_filter ::Host::Managed, :host_collection_ids => [],
@@ -413,4 +414,6 @@ Foreman::Plugin.register :katello do
413
414
  precompile.concat(bastion_locale_files)
414
415
 
415
416
  precompile_assets(precompile)
417
+
418
+ extend_observable_events(::Dynflow::Action.descendants.select { |klass| klass <= ::Actions::ObservableAction }.map(&:namespaced_event_names))
416
419
  end
@@ -4,27 +4,18 @@ namespace :katello do
4
4
  class BackendCleaner
5
5
  def initialize
6
6
  @candlepin_uuids = []
7
- @pulp_uuids = []
8
7
  @katello_candlepin_uuids = []
9
- @katello_pulp_uuids = []
10
8
  end
11
9
 
12
10
  def populate!
13
11
  @candlepin_uuids = Katello::Resources::Candlepin::Consumer.all_uuids
14
12
  @katello_candlepin_uuids = Katello::Host::SubscriptionFacet.pluck(:uuid).compact
15
-
16
- @pulp_uuids = ::Katello.pulp_server.extensions.consumer.retrieve_all.map { |consumer| consumer['id'] }
17
- @katello_pulp_uuids = Katello::Host::ContentFacet.pluck(:uuid).compact
18
13
  end
19
14
 
20
15
  def hosts_with_no_subscriptions
21
16
  ::Host.where(:id => Katello::Host::SubscriptionFacet.where(:uuid => @katello_candlepin_uuids - @candlepin_uuids).select(:host_id))
22
17
  end
23
18
 
24
- def hosts_with_no_content
25
- ::Host.where(:id => Katello::Host::ContentFacet.where(:uuid => @katello_pulp_uuids - @pulp_uuids).select(:host_id))
26
- end
27
-
28
19
  def hosts_with_nil_facets
29
20
  nil_sub = Katello::Host::SubscriptionFacet.where(:uuid => nil).select(:host_id).to_sql
30
21
  ::Host.where(" id in (#{nil_sub})")
@@ -33,10 +24,6 @@ namespace :katello do
33
24
  def cp_orphaned_host_uuids
34
25
  @candlepin_uuids - @katello_candlepin_uuids
35
26
  end
36
-
37
- def pulp_orphaned_host_uuids
38
- @pulp_uuids - @katello_pulp_uuids
39
- end
40
27
  end
41
28
 
42
29
  def cleanup_hosts(cleaner)
@@ -49,11 +36,6 @@ namespace :katello do
49
36
  print "Host #{host.id} #{host.name} #{host.subscription_facet.try(:uuid)} is partially missing subscription information. Un-registering\n"
50
37
  execute("Failed to delete host") { Katello::RegistrationManager.unregister_host(host, host_unregister_options(host)) }
51
38
  end
52
-
53
- cleaner.hosts_with_no_content.each do |host|
54
- print "Host #{host.id} #{host.name} #{host.content_facet.try(:uuid)} is partially missing content information. Un-registering\n"
55
- execute("Failed to delete host") { Katello::RegistrationManager.unregister_host(host, host_unregister_options(host)) }
56
- end
57
39
  end
58
40
 
59
41
  def clean_backend_orphans(cleaner)
@@ -63,13 +45,6 @@ namespace :katello do
63
45
  cp_uuids.each do |consumer_id|
64
46
  execute("exception when destroying candlepin consumer #{consumer_id}") { Katello::Resources::Candlepin::Consumer.destroy(consumer_id) }
65
47
  end
66
-
67
- pulp_uuids = cleaner.pulp_orphaned_host_uuids
68
- print "#{pulp_uuids.count} orphaned consumer id(s) found in pulp.\n"
69
- print "Pulp orphaned consumers: #{pulp_uuids}\n"
70
- pulp_uuids.each do |consumer_id|
71
- execute("exception when destroying pulp consumer #{consumer_id}") { Katello.pulp_server.extensions.consumer.delete(consumer_id) }
72
- end
73
48
  end
74
49
 
75
50
  def host_unregister_options(host)
@@ -1,3 +1,3 @@
1
1
  module Katello
2
- VERSION = "4.0.0".freeze
2
+ VERSION = "4.0.1".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katello
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - N/A
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-19 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -140,16 +140,16 @@ dependencies:
140
140
  name: qpid_proton
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ">="
143
+ - - "<"
144
144
  - !ruby/object:Gem::Version
145
- version: '0'
145
+ version: '0.34'
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ">="
150
+ - - "<"
151
151
  - !ruby/object:Gem::Version
152
- version: '0'
152
+ version: '0.34'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: stomp
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -354,20 +354,20 @@ dependencies:
354
354
  requirements:
355
355
  - - ">="
356
356
  - !ruby/object:Gem::Version
357
- version: 2.6.0
357
+ version: 2.9.0
358
358
  - - "<"
359
359
  - !ruby/object:Gem::Version
360
- version: 2.9.0
360
+ version: 2.10.0
361
361
  type: :runtime
362
362
  prerelease: false
363
363
  version_requirements: !ruby/object:Gem::Requirement
364
364
  requirements:
365
365
  - - ">="
366
366
  - !ruby/object:Gem::Version
367
- version: 2.6.0
367
+ version: 2.9.0
368
368
  - - "<"
369
369
  - !ruby/object:Gem::Version
370
- version: 2.9.0
370
+ version: 2.10.0
371
371
  - !ruby/object:Gem::Dependency
372
372
  name: pulp_rpm_client
373
373
  requirement: !ruby/object:Gem::Requirement
@@ -930,6 +930,8 @@ files:
930
930
  - app/lib/actions/katello/host/update_system_purpose.rb
931
931
  - app/lib/actions/katello/host/upload_package_profile.rb
932
932
  - app/lib/actions/katello/host/upload_profiles.rb
933
+ - app/lib/actions/katello/jail_concern/content_view.rb
934
+ - app/lib/actions/katello/jail_concern/organization.rb
933
935
  - app/lib/actions/katello/organization/create.rb
934
936
  - app/lib/actions/katello/organization/destroy.rb
935
937
  - app/lib/actions/katello/organization/manifest_delete.rb
@@ -5194,7 +5196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
5194
5196
  - !ruby/object:Gem::Version
5195
5197
  version: '0'
5196
5198
  requirements: []
5197
- rubygems_version: 3.1.4
5199
+ rubygems_version: 3.1.6
5198
5200
  signing_key:
5199
5201
  specification_version: 4
5200
5202
  summary: Content and Subscription Management plugin for Foreman