katello 3.16.0.rc5.1 → 3.16.0

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +39 -23
  3. data/app/helpers/katello/content_view_helper.rb +15 -0
  4. data/app/lib/actions/katello/content_view/incremental_updates.rb +3 -1
  5. data/app/lib/actions/katello/content_view/publish.rb +55 -16
  6. data/app/lib/actions/katello/content_view_version/incremental_update.rb +81 -51
  7. data/app/lib/actions/katello/repository/multi_clone_contents.rb +66 -0
  8. data/app/lib/actions/katello/repository/multi_clone_to_version.rb +30 -0
  9. data/app/lib/actions/pulp3/abstract_async_task.rb +62 -58
  10. data/app/lib/actions/pulp3/content_migration.rb +4 -0
  11. data/app/lib/actions/pulp3/orchestration/repository/copy_all_units.rb +1 -2
  12. data/app/lib/actions/pulp3/orchestration/repository/multi_copy_all_units.rb +36 -0
  13. data/app/lib/actions/pulp3/repository/multi_copy_content.rb +28 -0
  14. data/app/lib/actions/pulp3/repository/multi_copy_units.rb +14 -7
  15. data/app/lib/actions/pulp3/repository/save_version.rb +11 -3
  16. data/app/lib/actions/pulp3/repository/save_versions.rb +47 -13
  17. data/app/lib/katello/errors.rb +1 -15
  18. data/app/models/katello/content_view.rb +18 -6
  19. data/app/models/katello/content_view_erratum_filter.rb +13 -0
  20. data/app/models/katello/content_view_module_stream_filter.rb +19 -0
  21. data/app/models/katello/module_stream.rb +1 -1
  22. data/app/services/katello/pulp3/api/core.rb +4 -0
  23. data/app/services/katello/pulp3/erratum.rb +3 -1
  24. data/app/services/katello/pulp3/migration.rb +3 -2
  25. data/app/services/katello/pulp3/migration_plan.rb +6 -6
  26. data/app/services/katello/pulp3/repository.rb +10 -1
  27. data/app/services/katello/pulp3/repository/yum.rb +168 -35
  28. data/app/services/katello/pulp3/task.rb +100 -0
  29. data/app/services/katello/pulp3/task_group.rb +79 -0
  30. data/lib/katello/version.rb +1 -1
  31. data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
  32. metadata +17 -10
@@ -0,0 +1,66 @@
1
+ module Actions
2
+ module Katello
3
+ module Repository
4
+ class MultiCloneContents < Actions::Base
5
+ include Actions::Katello::PulpSelector
6
+ def plan(extended_repo_mapping, options)
7
+ generate_metadata = options.fetch(:generate_metadata, true)
8
+ copy_contents = options.fetch(:copy_contents, true)
9
+ solve_dependencies = options.fetch(:solve_dependencies, false)
10
+
11
+ sequence do
12
+ if copy_contents
13
+ plan_action(Pulp3::Orchestration::Repository::MultiCopyAllUnits,
14
+ extended_repo_mapping,
15
+ SmartProxy.pulp_master,
16
+ solve_dependencies: solve_dependencies)
17
+ end
18
+
19
+ extended_repo_mapping.each do |source_repos, dest_repo_map|
20
+ if generate_metadata
21
+ metadata_generate(source_repos, dest_repo_map[:dest_repo], dest_repo_map[:filters])
22
+ end
23
+ end
24
+
25
+ extended_repo_mapping.values.each do |dest_repo_map|
26
+ plan_action(Katello::Repository::IndexContent, id: dest_repo_map[:dest_repo].id)
27
+ end
28
+ end
29
+ end
30
+
31
+ def metadata_generate(source_repositories, new_repository, filters)
32
+ metadata_options = {}
33
+
34
+ if source_repositories.count == 1 && filters.empty?
35
+ metadata_options[:source_repository] = source_repositories.first
36
+ end
37
+
38
+ check_matching_content = ::Katello::RepositoryTypeManager.find(new_repository.content_type).metadata_publish_matching_check
39
+ if new_repository.environment && source_repositories.count == 1 && check_matching_content
40
+ match_check_output = plan_action(Katello::Repository::CheckMatchingContent,
41
+ :source_repo_id => source_repositories.first.id,
42
+ :target_repo_id => new_repository.id).output
43
+
44
+ metadata_options[:matching_content] = match_check_output[:matching_content]
45
+ end
46
+
47
+ plan_action(Katello::Repository::MetadataGenerate, new_repository, metadata_options)
48
+ unless source_repositories.first.saved_checksum_type == new_repository.saved_checksum_type
49
+ checksum_mapping = {}
50
+ repository_mapping.each do |source_repos, dest_repo|
51
+ checksum_mapping[dest_repo.id] = source_repos.first.saved_checksum_type
52
+ end
53
+ plan_self(:checksum_mapping => checksum_mapping)
54
+ end
55
+ end
56
+
57
+ def finalize
58
+ input[:checksum_mapping].each do |repo_id, checksum_type|
59
+ repository = ::Katello::Repository.find(repo_id)
60
+ repository.update!(saved_checksum_type: checksum_type) if (repository && checksum_type)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,30 @@
1
+ module Actions
2
+ module Katello
3
+ module Repository
4
+ class MultiCloneToVersion < Actions::Base
5
+ def plan(repository_mapping, content_view_version, options = {})
6
+ incremental = options.fetch(:incremental, false)
7
+ content_view = content_view_version.content_view
8
+ extended_repo_map = extended_repo_mapping(repository_mapping, content_view, incremental)
9
+ sequence do
10
+ plan_action(::Actions::Katello::Repository::MultiCloneContents, extended_repo_map,
11
+ copy_contents: true,
12
+ solve_dependencies: content_view.solve_dependencies,
13
+ metadata_generate: !incremental)
14
+ end
15
+ end
16
+
17
+ def extended_repo_mapping(repo_map, content_view, incremental)
18
+ # Example: {[source_repos] => {dest_repo: dest_repo, filters: filters}}
19
+ extended_repo_map = {}
20
+ repo_map.each do |source_repos, dest_repo|
21
+ filters = incremental ? [] : content_view.filters.applicable(source_repos.first)
22
+ extended_repo_map[source_repos] = { :dest_repo => dest_repo,
23
+ :filters => filters }
24
+ end
25
+ extended_repo_map
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,41 +4,6 @@ module Actions
4
4
  include Actions::Base::Polling
5
5
  include ::Dynflow::Action::Cancellable
6
6
 
7
- WAITING = ['waiting',
8
- SKIPPED = 'skipped'.freeze,
9
- RUNNING = 'running'.freeze,
10
- COMPLETED = 'completed'.freeze,
11
- FAILED = 'failed'.freeze,
12
- CANCELED = 'canceled'.freeze].freeze
13
-
14
- FINISHED_STATES = [COMPLETED, FAILED, CANCELED, SKIPPED].freeze
15
-
16
- # A call report Looks like: {"task":"/pulp/api/v3/tasks/5/"}
17
- # {
18
- # "pulp_href":"/pulp/api/v3/tasks/4/",
19
- # "pulp_created":"2019-02-21T19:50:40.476767Z",
20
- # "job_id":"d0359658-d926-47a2-b430-1b2092b3bd86",
21
- # "state":"completed",
22
- # "name":"pulp_file.app.tasks.publishing.publish",
23
- # "started_at":"2019-02-21T19:50:40.556002Z",
24
- # "finished_at":"2019-02-21T19:50:40.618397Z",
25
- # "non_fatal_errors":[
26
- #
27
- # ],
28
- # "error":null,
29
- # "worker":"/pulp/api/v3/workers/1/",
30
- # "parent":null,
31
- # "spawned_tasks":[
32
- #
33
- # ],
34
- # "progress_reports":[
35
- #
36
- # ],
37
- # "created_resources":[
38
- # "/pulp/api/v3/publications/1/"
39
- # ]
40
- # }
41
-
42
7
  def run(event = nil)
43
8
  # do nothing when the action is being skipped
44
9
  unless event == Dynflow::Action::Skip
@@ -49,16 +14,16 @@ module Actions
49
14
  def humanized_state
50
15
  case state
51
16
  when :running
52
- if self.external_task.nil?
17
+ if self.combined_tasks.empty?
53
18
  _("initiating Pulp task")
54
19
  else
55
20
  _("checking Pulp task status")
56
21
  end
57
22
  when :suspended
58
- if external_task&.all? { |task| task[:start_time].nil? }
59
- _("waiting for Pulp to start the task")
60
- else
23
+ if combined_tasks.any?(&:started?)
61
24
  _("waiting for Pulp to finish the task")
25
+ else
26
+ _("waiting for Pulp to start the task")
62
27
  end
63
28
  else
64
29
  super
@@ -66,28 +31,49 @@ module Actions
66
31
  end
67
32
 
68
33
  def done?
69
- external_task&.all? { |task| task[:finish_time] || FINISHED_STATES.include?(task[:state]) }
34
+ combined_tasks&.all? { |task| task.done? }
70
35
  end
71
36
 
72
37
  def external_task
73
- output[:pulp_tasks]
38
+ #this must return nil until external_task= is called
39
+ combined_tasks
40
+ end
41
+
42
+ def combined_tasks
43
+ return nil if pulp_tasks.nil? || task_groups.nil?
44
+ pulp_tasks + task_groups
45
+ end
46
+
47
+ def pulp_tasks
48
+ return nil if output[:pulp_tasks].nil?
49
+ output[:pulp_tasks] = new_or_existing_objects(::Katello::Pulp3::Task, output[:pulp_tasks])
50
+ end
51
+
52
+ def task_groups
53
+ return nil if output[:task_groups].nil?
54
+ output[:task_groups] = new_or_existing_objects(::Katello::Pulp3::TaskGroup, output[:task_groups])
55
+ end
56
+
57
+ def new_or_existing_objects(object_class, objects)
58
+ objects.map do |object|
59
+ if object.is_a?(object_class)
60
+ object
61
+ else
62
+ object_class.new(smart_proxy, object)
63
+ end
64
+ end
74
65
  end
75
66
 
76
67
  def cancel!
77
68
  cancel
78
- self.external_task = poll_external_task
69
+ poll_external_task
79
70
  # We suspend the action and the polling will take care of finding
80
71
  # out if the cancelling was successful
81
72
  suspend unless done?
82
73
  end
83
74
 
84
75
  def cancel
85
- output[:pulp_tasks].each do |pulp_task|
86
- data = PulpcoreClient::Task.new(state: 'canceled')
87
- tasks_api.tasks_cancel(pulp_task['pulp_href'], data)
88
- #the main task may have completed, so cancel spawned tasks too
89
- pulp_task['spawned_tasks']&.each { |spawned| tasks_api.tasks_cancel(spawned['pulp_href'], data) }
90
- end
76
+ pulp_tasks.each { |task| task.cancel }
91
77
  end
92
78
 
93
79
  def rescue_external_task(error)
@@ -109,24 +95,42 @@ module Actions
109
95
  response
110
96
  end
111
97
 
98
+ def check_for_errors
99
+ combined_tasks.each do |task|
100
+ if (message = task.error)
101
+ fail ::Katello::Errors::Pulp3Error, message
102
+ end
103
+ end
104
+ end
105
+
112
106
  def external_task=(external_task_data)
113
- output[:pulp_tasks] = transform_task_response(external_task_data)
114
- output[:pulp_tasks].each do |pulp_task|
115
- if (pulp_exception = ::Katello::Errors::Pulp3Error.from_task(pulp_task))
116
- fail pulp_exception
107
+ #currently we assume everything coming from invoke_external_task_methods are tasks
108
+ tasks = transform_task_response(external_task_data)
109
+ output[:pulp_tasks] = new_or_existing_objects(::Katello::Pulp3::Task, tasks)
110
+
111
+ add_task_groups
112
+ check_for_errors
113
+ end
114
+
115
+ def add_task_groups
116
+ output[:task_groups] ||= []
117
+ pulp_tasks.each do |task|
118
+ if task.task_group_href && !tracking_task_group?(task.task_group_href)
119
+ output[:task_groups] << ::Katello::Pulp3::TaskGroup.new_from_href(smart_proxy, task.task_group_href)
117
120
  end
118
121
  end
119
122
  end
120
123
 
121
- def tasks_api
122
- ::Katello::Pulp3::Api::Core.new(smart_proxy).tasks_api
124
+ def tracking_task_group?(href)
125
+ task_groups&.any? { |group| group.href == href }
123
126
  end
124
127
 
125
128
  def poll_external_task
126
- external_task.map do |task|
127
- task = tasks_api.read(task['pulp_href'] || task['task'])
128
- task.as_json
129
- end
129
+ pulp_tasks.each(&:poll)
130
+ output[:task_groups] = task_groups.each(&:poll) if task_groups
131
+ add_task_groups
132
+ check_for_errors
133
+ pulp_tasks
130
134
  end
131
135
  end
132
136
  end
@@ -12,6 +12,10 @@ module Actions
12
12
  migration_service = ::Katello::Pulp3::Migration.new(smart_proxy)
13
13
  migration_service.create_and_run_migrations
14
14
  end
15
+
16
+ def rescue_strategy
17
+ Dynflow::Action::Rescue::Skip
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -21,8 +21,7 @@ module Actions
21
21
  else
22
22
  #if we are not filtering, copy the version to the cv repository, and the units for each additional repo
23
23
  action = plan_action(Actions::Pulp3::Repository::CopyVersion, source_repositories.first, smart_proxy, target_repo)
24
- plan_action(Actions::Pulp3::Repository::SaveVersion, target_repo,
25
- repository_details: { latest_version_href: action.output[:latest_version_output] }, tasks: action.output[:pulp_tasks])
24
+ plan_action(Actions::Pulp3::Repository::SaveVersion, target_repo, tasks: action.output[:pulp_tasks])
26
25
  copy_actions = []
27
26
  #since we're creating a new version from the first repo, start copying at the 2nd
28
27
  source_repositories[1..-1].each do |source_repo|
@@ -0,0 +1,36 @@
1
+ module Actions
2
+ module Pulp3
3
+ module Orchestration
4
+ module Repository
5
+ class MultiCopyAllUnits < Pulp3::Abstract
6
+ def plan(extended_repo_map, smart_proxy, options = {})
7
+ solve_dependencies = options.fetch(:solve_dependencies, false)
8
+ if extended_repo_map.values.pluck(:filters).flatten.present? ||
9
+ extended_repo_map.keys.detect { |source_repos| source_repos.length > 1 }
10
+ sequence do
11
+ copy_action = plan_action(Actions::Pulp3::Repository::MultiCopyContent, extended_repo_map, smart_proxy,
12
+ solve_dependencies: solve_dependencies)
13
+ plan_action(Actions::Pulp3::Repository::SaveVersions, extended_repo_map.values.pluck(:dest_repo),
14
+ tasks: copy_action.output[:pulp_tasks])
15
+ end
16
+ else
17
+ repo_id_map = {}
18
+ extended_repo_map.each do |source_repos, dest_repo_map|
19
+ repo_id_map[source_repos.first.id] = dest_repo_map[:dest_repo].id
20
+ end
21
+ plan_self(repo_id_map: repo_id_map)
22
+ end
23
+ end
24
+
25
+ def run
26
+ input[:repo_id_map].each do |source_repo_id, dest_repo_id|
27
+ dest_repo = ::Katello::Repository.find(dest_repo_id)
28
+ source_repo = ::Katello::Repository.find(source_repo_id)
29
+ dest_repo.update!(version_href: source_repo.version_href)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ module Actions
2
+ module Pulp3
3
+ module Repository
4
+ class MultiCopyContent < Pulp3::AbstractAsyncTask
5
+ def plan(extended_repo_map, smart_proxy, options)
6
+ repo_id_map = {}
7
+
8
+ extended_repo_map.each do |source_repos, dest_repo_map|
9
+ repo_id_map[source_repos&.map(&:id)] = { :dest_repo => dest_repo_map[:dest_repo].id,
10
+ :filter_ids => dest_repo_map[:filters]&.map(&:id) }
11
+ end
12
+
13
+ plan_self(options.merge(:repo_id_map => repo_id_map, :smart_proxy_id => smart_proxy.id))
14
+ end
15
+
16
+ def invoke_external_task
17
+ repo_id_map = {}
18
+
19
+ input[:repo_id_map].each do |source_repo_ids, dest_repo_map|
20
+ repo_id_map[JSON.parse(source_repo_ids)] = dest_repo_map
21
+ end
22
+
23
+ output[:pulp_tasks] = ::Katello::Repository.find(repo_id_map.values.first[:dest_repo]).backend_service(smart_proxy).copy_content_from_mapping(repo_id_map, input)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -3,7 +3,7 @@ module Actions
3
3
  module Repository
4
4
  class MultiCopyUnits < Pulp3::AbstractAsyncTask
5
5
  # repo_map example: {
6
- # <source_repo_id>: {
6
+ # [<source_repo_ids>]: {
7
7
  # dest_repo: <dest_repo_id>,
8
8
  # base_version: <base_version>
9
9
  # }
@@ -22,7 +22,11 @@ module Actions
22
22
 
23
23
  def invoke_external_task
24
24
  unit_hrefs = []
25
- repo_map = input[:repo_map]
25
+ repo_map = {}
26
+
27
+ input[:repo_map].each do |source_repo_ids, dest_repo_map|
28
+ repo_map[JSON.parse(source_repo_ids)] = dest_repo_map
29
+ end
26
30
 
27
31
  if input[:unit_map][:errata].any?
28
32
  unit_hrefs << ::Katello::RepositoryErratum.
@@ -34,13 +38,16 @@ module Actions
34
38
  if input[:unit_map][:rpms].any?
35
39
  unit_hrefs << ::Katello::Rpm.where(:id => input[:unit_map][:rpms]).map(&:pulp_id)
36
40
  end
41
+ unit_hrefs.flatten!
42
+
43
+ repo_map.each do |_source_repos, dest_repo_map|
44
+ dest_repo_map[:content_unit_hrefs] = unit_hrefs
45
+ end
37
46
 
38
- # TODO: Fix this workaround by refactoring copy_units after general content view dep solving is refactored
39
- source_repo = ::Katello::Repository.find(repo_map.keys.first)
40
47
  target_repo = ::Katello::Repository.find(repo_map.values.first[:dest_repo])
41
- dest_base_version = repo_map.values.first[:base_version]
42
- repo_map.delete(repo_map.keys.first)
43
- output[:pulp_tasks] = target_repo.backend_service(SmartProxy.pulp_master).copy_units(source_repo, unit_hrefs.flatten, input[:dependency_solving], dest_base_version, repo_map)
48
+ unless unit_hrefs.flatten.empty?
49
+ output[:pulp_tasks] = target_repo.backend_service(SmartProxy.pulp_master).multi_copy_units(repo_map, input[:dependency_solving])
50
+ end
44
51
  end
45
52
  end
46
53
  end
@@ -9,12 +9,20 @@ module Actions
9
9
  def run
10
10
  repo = ::Katello::Repository.find(input[:repository_id])
11
11
 
12
- if input[:tasks]
12
+ if input[:tasks].present?
13
13
  version_href = input[:tasks].last[:created_resources].first
14
14
  end
15
15
 
16
- if !version_href && input[:repository_details]
17
- version_href = input[:repository_details][:latest_version_href]
16
+ unless version_href
17
+ if input[:repository_details]
18
+ version_href = input[:repository_details][:latest_version_href]
19
+ elsif repo.version_href.nil?
20
+ # Fetch latest Pulp 3 repo version
21
+ repo_backend_service = repo.backend_service(SmartProxy.pulp_master)
22
+ version_href ||= repo_backend_service.api.
23
+ repositories_api.read(repo_backend_service.
24
+ repository_reference.repository_href).latest_version_href
25
+ end
18
26
  end
19
27
 
20
28
  if version_href
@@ -7,23 +7,34 @@ module Actions
7
7
  end
8
8
 
9
9
  def run
10
+ return if input[:tasks].empty?
10
11
  version_hrefs = input[:tasks].last[:created_resources]
11
- repositories = input[:repository_ids].collect do |repo_id|
12
- ::Katello::Repository.find(repo_id)
13
- end
12
+ repositories = find_repositories(input[:repository_ids])
14
13
 
15
- output[:contents_changed] = false
16
- output[:updated_repositories] = []
14
+ output.merge!(contents_changed: false, updated_repositories: [])
17
15
  repositories.each do |repo|
18
- # Chop off the version number to compare base repo strings
19
- unversioned_href = repo.version_href[0..-2].rpartition('/').first
20
- new_version_href = version_hrefs.detect do |version_href|
21
- unversioned_href == version_href[0..-2].rpartition('/').first
16
+ repo_backend_service = repo.backend_service(SmartProxy.pulp_master)
17
+ if repo.version_href
18
+ # Chop off the version number to compare base repo strings
19
+ unversioned_href = repo.version_href[0..-2].rpartition('/').first
20
+ # Could have multiple version_hrefs for the same repo depending on the copy task
21
+ new_version_hrefs = version_hrefs.collect do |version_href|
22
+ version_href if unversioned_href == version_href[0..-2].rpartition('/').first
23
+ end
24
+
25
+ new_version_hrefs.compact!
26
+ if new_version_hrefs.size > 1
27
+ # Find latest version_href by its version number
28
+ new_version_href = version_map(new_version_hrefs).max_by { |_href, version| version }.first
29
+ else
30
+ new_version_href = new_version_hrefs.first
31
+ end
32
+
33
+ # Successive incremental updates won't generate a new repo version, so fetch the latest Pulp 3 repo version
34
+ new_version_href ||= latest_version_href(repo_backend_service)
35
+ else
36
+ new_version_href = latest_version_href(repo_backend_service)
22
37
  end
23
- # Successive incremental updates won't generate a new repo version, so fetch the latest Pulp 3 repo version
24
- new_version_href ||= ::Katello::Pulp3::Api::Yum.new(SmartProxy.pulp_master!).
25
- repositories_api.read(repo.backend_service(SmartProxy.pulp_master).
26
- repository_reference.repository_href).latest_version_href
27
38
 
28
39
  unless new_version_href == repo.version_href
29
40
  repo.update(version_href: new_version_href)
@@ -33,6 +44,29 @@ module Actions
33
44
  end
34
45
  end
35
46
  end
47
+
48
+ def version_map(version_hrefs)
49
+ version_map = {}
50
+ version_hrefs.each do |href|
51
+ version_map[href] = href.split("/")[-1].to_i
52
+ end
53
+ version_map
54
+ end
55
+
56
+ def latest_version_href(repo_backend_service)
57
+ repo_backend_service.api.repositories_api.
58
+ read(repo_backend_service.repository_reference.repository_href).latest_version_href
59
+ end
60
+
61
+ def find_repositories(repository_ids)
62
+ repository_ids.collect do |repo_id|
63
+ if repo_id.is_a?(Hash)
64
+ ::Katello::Repository.find(repo_id.with_indifferent_access[:id])
65
+ else
66
+ ::Katello::Repository.find(repo_id)
67
+ end
68
+ end
69
+ end
36
70
  end
37
71
  end
38
72
  end