katello 4.13.0.rc1 → 4.13.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +334 -23
  3. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +8 -0
  4. data/app/controllers/katello/api/v2/repositories_controller.rb +1 -1
  5. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +7 -2
  6. data/app/lib/actions/katello/organization/manifest_delete.rb +6 -1
  7. data/app/lib/actions/katello/repository/create.rb +17 -11
  8. data/app/lib/actions/katello/repository/create_root.rb +4 -2
  9. data/app/lib/actions/katello/repository/discover.rb +11 -4
  10. data/app/lib/actions/katello/upstream_subscriptions/bind_entitlement.rb +1 -1
  11. data/app/lib/actions/pulp3/orchestration/orphan_cleanup/remove_orphans.rb +1 -0
  12. data/app/lib/actions/pulp3/orphan_cleanup/purge_completed_tasks.rb +15 -0
  13. data/app/lib/actions/pulp3/repository/create_publication.rb +4 -0
  14. data/app/lib/katello/repo_discovery.rb +4 -190
  15. data/app/lib/katello/resources/discovery/container.rb +127 -0
  16. data/app/lib/katello/resources/discovery/yum.rb +95 -0
  17. data/app/lib/katello/util/http_helper.rb +15 -0
  18. data/app/models/732bd3db9f64c621c64d2be4f2a838727aac0845.patch +61 -0
  19. data/app/models/katello/content_view.rb +2 -0
  20. data/app/models/katello/glue/pulp/repos.rb +8 -1
  21. data/app/models/katello/repository.rb +5 -1
  22. data/app/models/katello/repository.rb.bak +978 -0
  23. data/app/models/katello/root_repository.rb +14 -2
  24. data/app/models/katello/trace_status.rb +1 -1
  25. data/app/services/katello/pulp3/api/core.rb +8 -0
  26. data/app/services/katello/pulp3/api/docker.rb +4 -0
  27. data/app/services/katello/pulp3/content_view_version/import_validator.rb.bak +166 -0
  28. data/app/services/katello/pulp3/content_view_version/importable_repositories.rb.bak +164 -0
  29. data/app/services/katello/pulp3/repository/yum.rb +1 -6
  30. data/app/services/katello/repository_type.rb +1 -1
  31. data/app/views/foreman/smart_proxies/_content_tab.html.erb +3 -1
  32. data/config/initializers/monkeys.rb +0 -1
  33. data/db/migrate/20240520142245_add_container_push_props_to_repo.rb +7 -0
  34. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/checksum.service.js +6 -1
  35. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +3 -0
  36. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +0 -3
  37. data/lib/katello/plugin.rb +12 -0
  38. data/lib/katello/repository_types/docker.rb +1 -0
  39. data/lib/katello/repository_types/yum.rb +1 -0
  40. data/lib/katello/tasks/update_repository_expiry.rake +114 -0
  41. data/lib/katello/version.rb +1 -1
  42. data/lib/katello.rb +0 -2
  43. data/locale/bn/katello.po.time_stamp +0 -0
  44. data/locale/bn_IN/katello.po.time_stamp +0 -0
  45. data/locale/ca/katello.po.time_stamp +0 -0
  46. data/locale/cs/katello.po.time_stamp +0 -0
  47. data/locale/cs_CZ/katello.po.time_stamp +0 -0
  48. data/locale/de/katello.po.time_stamp +0 -0
  49. data/locale/de_AT/katello.po.time_stamp +0 -0
  50. data/locale/de_DE/katello.po.time_stamp +0 -0
  51. data/locale/el/katello.po.time_stamp +0 -0
  52. data/locale/en/katello.po.time_stamp +0 -0
  53. data/locale/en_GB/katello.po.time_stamp +0 -0
  54. data/locale/en_US/katello.po.time_stamp +0 -0
  55. data/locale/es/katello.po.time_stamp +0 -0
  56. data/locale/et_EE/katello.po.time_stamp +0 -0
  57. data/locale/fr/katello.po.time_stamp +0 -0
  58. data/locale/gl/katello.po.time_stamp +0 -0
  59. data/locale/gu/katello.po.time_stamp +0 -0
  60. data/locale/he_IL/katello.po.time_stamp +0 -0
  61. data/locale/hi/katello.po.time_stamp +0 -0
  62. data/locale/id/katello.po.time_stamp +0 -0
  63. data/locale/it/katello.po.time_stamp +0 -0
  64. data/locale/ja/katello.po.time_stamp +0 -0
  65. data/locale/ka/katello.po.time_stamp +0 -0
  66. data/locale/kn/katello.po.time_stamp +0 -0
  67. data/locale/ko/katello.po.time_stamp +0 -0
  68. data/locale/ml_IN/katello.po.time_stamp +0 -0
  69. data/locale/mr/katello.po.time_stamp +0 -0
  70. data/locale/nl_NL/katello.po.time_stamp +0 -0
  71. data/locale/or/katello.po.time_stamp +0 -0
  72. data/locale/pa/katello.po.time_stamp +0 -0
  73. data/locale/pl/katello.po.time_stamp +0 -0
  74. data/locale/pl_PL/katello.po.time_stamp +0 -0
  75. data/locale/pt/katello.po.time_stamp +0 -0
  76. data/locale/pt_BR/katello.po.time_stamp +0 -0
  77. data/locale/ro/katello.po.time_stamp +0 -0
  78. data/locale/ro_RO/katello.po.time_stamp +0 -0
  79. data/locale/ru/katello.po.time_stamp +0 -0
  80. data/locale/sl/katello.po.time_stamp +0 -0
  81. data/locale/sv_SE/katello.po.time_stamp +0 -0
  82. data/locale/ta/katello.po.time_stamp +0 -0
  83. data/locale/ta_IN/katello.po.time_stamp +0 -0
  84. data/locale/te/katello.po.time_stamp +0 -0
  85. data/locale/tr/katello.po.time_stamp +0 -0
  86. data/locale/vi/katello.po.time_stamp +0 -0
  87. data/locale/vi_VN/katello.po.time_stamp +0 -0
  88. data/locale/zh/katello.po.time_stamp +0 -0
  89. data/locale/zh_CN/katello.po.time_stamp +0 -0
  90. data/locale/zh_TW/katello.po.time_stamp +0 -0
  91. data/package.json +0 -1
  92. data/webpack/components/Content/ContentTable.js +0 -1
  93. data/webpack/components/Content/__tests__/__snapshots__/ContentTable.test.js.snap +0 -1
  94. data/webpack/global_test_setup.js.bak +59 -0
  95. data/webpack/scenes/ModuleStreams/ModuleStreamsPage.js +33 -39
  96. data/webpack/scenes/ModuleStreams/__tests__/ModuleStreamPage.test.js +4 -2
  97. data/webpack/scenes/ModuleStreams/__tests__/__snapshots__/ModuleStreamsTable.test.js.snap +0 -1
  98. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +4 -2
  99. metadata +87 -28
  100. data/lib/monkeys/anemone.rb +0 -33
  101. data/webpack/utils/__tests__/useParamsWithHash.test.js +0 -22
  102. data/webpack/utils/paramsFromHash.js +0 -16
  103. data/webpack/utils/useUrlParams.js +0 -14
@@ -4,22 +4,11 @@ module Katello
4
4
  class RepoDiscovery
5
5
  include Katello::Util::HttpProxy
6
6
 
7
- attr_reader :found, :crawled, :to_follow
8
-
9
- # rubocop:disable Metrics/ParameterLists
10
- def initialize(url, content_type = 'yum', upstream_username = nil,
11
- upstream_password = nil, search = '*', crawled = [],
12
- found = [], to_follow = [])
13
- @uri = uri(url)
14
- @content_type = content_type
15
- @upstream_username = upstream_username.empty? ? nil : upstream_username
16
- @upstream_password = upstream_password.empty? ? nil : upstream_password
17
- @search = search
18
- @found = found
19
- @crawled = crawled
20
- @to_follow = to_follow
7
+ def self.class_for(content_type)
8
+ repo_discovery_class = RepositoryTypeManager.find_repository_type(content_type)&.repo_discovery_class
9
+ fail _("Content type does not support repo discovery") unless repo_discovery_class
10
+ repo_discovery_class
21
11
  end
22
- # rubocop:enable Metrics/ParameterLists
23
12
 
24
13
  def uri(url)
25
14
  #add a / on the end, as directories require it or else
@@ -27,180 +16,5 @@ module Katello
27
16
  url += '/' unless url.ends_with?('/')
28
17
  URI(url)
29
18
  end
30
-
31
- def run(resume_point)
32
- if @content_type == 'docker'
33
- docker_search
34
- else
35
- if @uri.scheme == 'file'
36
- file_crawl(uri(resume_point))
37
- elsif %w(http https).include?(@uri.scheme)
38
- http_crawl(uri(resume_point))
39
- else
40
- fail _("Unsupported URL protocol %s.") % @uri.scheme
41
- end
42
- end
43
- end
44
-
45
- private
46
-
47
- def parse_parameter(field_value)
48
- field_value.lstrip!
49
- i = field_value.index(/[ \t=;,]/) || field_value.length
50
- name = field_value.slice!(0, i).downcase(:ascii)
51
- field_value.lstrip!
52
- if field_value.delete_prefix!('=')
53
- field_value.lstrip!
54
- if field_value.delete_prefix!('"')
55
- value = ''
56
- until field_value.empty?
57
- break if field_value.delete_prefix!('"')
58
- field_value.delete_prefix!("\\")
59
- value += field_value.slice!(0, 1) || ''
60
- end
61
- else
62
- i = field_value.index(/[;,]/) || field_value.length
63
- value = field_value.slice!(0, i)
64
- end
65
- end
66
- {name: name, value: value || ''}
67
- end
68
-
69
- def parse_parameters(field_value)
70
- seen_rel = false
71
- has_next_rel = false
72
- has_anchor = false
73
- until field_value.empty?
74
- field_value.lstrip!
75
- break if field_value.delete_prefix!(';').nil?
76
- param = parse_parameter(field_value)
77
- case
78
- when param[:name] == 'rel' && !seen_rel
79
- seen_rel = true
80
- has_next_rel = param[:value].downcase(:ascii).split(/[ \t]/).include?('next')
81
- when param[:name] == 'anchor'
82
- has_anchor = true
83
- end
84
- end
85
- {has_next_rel: has_next_rel, has_anchor: has_anchor}
86
- end
87
-
88
- def get_next_link(link_header)
89
- # This code mostly follows Appendix B "Algorithms of Parsing Link Header Fields" of RFC 8288
90
- # "Web Linking", <https://www.rfc-editor.org/rfc/rfc8288#appendix-B> (that RFC appears to be
91
- # silent about multiple "next" links, so just use the first one and ignore any additional
92
- # ones, in the general spirit of being lenient):
93
- return nil if link_header.nil?
94
- field_value = link_header.clone
95
- until field_value.empty?
96
- # The following ignores any junk preceding the next <...> link URL:
97
- m = field_value.match(/<(.*)>/)
98
- break unless m
99
- target_string = m[1]
100
- field_value = m.post_match
101
- params = parse_parameters(field_value)
102
- if params[:has_next_rel]
103
- # To keep it simple, ignore a link with an (unlikely) anchor parameter; but the RFC
104
- # mandates that we "MUST NOT process the link without applying the anchor", so just raise
105
- # an exception in that (unlikely) case:
106
- fail "anchor not supported" if params[:has_anchor]
107
- return target_string
108
- end
109
- end
110
- nil
111
- end
112
-
113
- def docker_search
114
- request_params = {
115
- method: :get,
116
- headers: { accept: :json },
117
- url: "#{@uri}v1/search?q=#{@search}"
118
- }
119
-
120
- request_params[:user] = @upstream_username unless @upstream_username.empty?
121
- request_params[:password] = @upstream_password unless @upstream_password.empty?
122
- request_params[:proxy] = proxy_uri if proxy
123
-
124
- begin
125
- results = RestClient::Request.execute(request_params)
126
- JSON.parse(results)['results'].each do |result|
127
- @found << result['name']
128
- end
129
- rescue
130
- # Note: v2 endpoint does not support search
131
- request_params[:url] = "#{@uri}v2/_catalog"
132
- loop do
133
- results = RestClient::Request.execute(request_params)
134
- JSON.parse(results)['repositories'].each do |result|
135
- @found << result
136
- end
137
- next_uri = get_next_link(results.headers[:link])
138
- break if next_uri.nil?
139
- request_params[:url] = URI(request_params[:url]).merge(next_uri).to_s
140
- end
141
- end
142
- @found.sort!
143
- end
144
-
145
- def anemone_proxy_details
146
- details = {}
147
-
148
- if proxy
149
- details[:proxy_host] = proxy_host
150
- details[:proxy_port] = proxy_port
151
- details[:proxy_user] = proxy.username
152
- details[:proxy_password] = proxy.password
153
- end
154
-
155
- details
156
- end
157
-
158
- def http_crawl(resume_point)
159
- resume_point_uri = URI(resume_point)
160
- resume_point_uri.user = @upstream_username if @upstream_username
161
- resume_point_uri.password = @upstream_password if @upstream_password
162
-
163
- Anemone.crawl(resume_point_uri, anemone_proxy_details) do |anemone|
164
- anemone.focus_crawl do |page|
165
- @crawled << page.url.path
166
-
167
- page.links.each do |link|
168
- if link.path.ends_with?('/repodata/')
169
- page_url = page.url.clone
170
- page_url.user = nil
171
- page_url.password = nil
172
- @found << page_url.to_s
173
- else
174
- @to_follow << link.to_s if should_follow?(link.path)
175
- end
176
- end
177
- page.discard_doc! #saves memory, doc not needed
178
- []
179
- end
180
- end
181
- end
182
-
183
- def file_crawl(resume_point)
184
- if resume_point.path.ends_with?('/repodata/')
185
- found_path = Pathname(resume_point.path).parent.to_s
186
- @found << "file://#{found_path}"
187
- end
188
- if resume_point.path == @uri.path
189
- Dir.glob("#{@uri.path}**/").each { |path| @to_follow << path }
190
- @to_follow.shift
191
- end
192
- @crawled << resume_point.path
193
- end
194
-
195
- def should_follow?(path)
196
- #Verify:
197
- # * link's path starts with the base url
198
- # * link hasn't already been crawled
199
- # * link ends with '/' so it should be a directory
200
- # * link doesn't end with '/Packages/', as this increases
201
- # processing time and memory usage considerably
202
- return path.starts_with?(@uri.path) && !@crawled.include?(path) &&
203
- path.ends_with?('/') && !path.ends_with?('/Packages/')
204
- end
205
19
  end
206
20
  end
@@ -0,0 +1,127 @@
1
+ module Katello
2
+ module Resources
3
+ module Discovery
4
+ class Container < RepoDiscovery
5
+ attr_reader :found, :crawled, :to_follow
6
+ def initialize(url, crawled = [], found = [], to_follow = [],
7
+ upstream_credentials_and_search = {
8
+ upstream_username: nil,
9
+ upstream_password: nil,
10
+ search: '*'
11
+ })
12
+ @uri = uri(url)
13
+ @upstream_username = upstream_credentials_and_search[:upstream_username].presence
14
+ @upstream_password = upstream_credentials_and_search[:upstream_password].presence
15
+ @search = upstream_credentials_and_search.fetch(:search, '*')
16
+ @found = found
17
+ @crawled = crawled
18
+ @to_follow = to_follow
19
+ end
20
+
21
+ def run(_resume_point)
22
+ docker_search
23
+ end
24
+
25
+ private
26
+
27
+ def parse_parameter(field_value)
28
+ field_value.lstrip!
29
+ i = field_value.index(/[ \t=;,]/) || field_value.length
30
+ name = field_value.slice!(0, i).downcase(:ascii)
31
+ field_value.lstrip!
32
+ if field_value.delete_prefix!('=')
33
+ field_value.lstrip!
34
+ if field_value.delete_prefix!('"')
35
+ value = ''
36
+ until field_value.empty?
37
+ break if field_value.delete_prefix!('"')
38
+ field_value.delete_prefix!("\\")
39
+ value += field_value.slice!(0, 1) || ''
40
+ end
41
+ else
42
+ i = field_value.index(/[;,]/) || field_value.length
43
+ value = field_value.slice!(0, i)
44
+ end
45
+ end
46
+ {name: name, value: value || ''}
47
+ end
48
+
49
+ def parse_parameters(field_value)
50
+ seen_rel = false
51
+ has_next_rel = false
52
+ has_anchor = false
53
+ until field_value.empty?
54
+ field_value.lstrip!
55
+ break if field_value.delete_prefix!(';').nil?
56
+ param = parse_parameter(field_value)
57
+ case
58
+ when param[:name] == 'rel' && !seen_rel
59
+ seen_rel = true
60
+ has_next_rel = param[:value].downcase(:ascii).split(/[ \t]/).include?('next')
61
+ when param[:name] == 'anchor'
62
+ has_anchor = true
63
+ end
64
+ end
65
+ {has_next_rel: has_next_rel, has_anchor: has_anchor}
66
+ end
67
+
68
+ def get_next_link(link_header)
69
+ # This code mostly follows Appendix B "Algorithms of Parsing Link Header Fields" of RFC 8288
70
+ # "Web Linking", <https://www.rfc-editor.org/rfc/rfc8288#appendix-B> (that RFC appears to be
71
+ # silent about multiple "next" links, so just use the first one and ignore any additional
72
+ # ones, in the general spirit of being lenient):
73
+ return nil if link_header.nil?
74
+ field_value = link_header.clone
75
+ until field_value.empty?
76
+ # The following ignores any junk preceding the next <...> link URL:
77
+ m = field_value.match(/<(.*)>/)
78
+ break unless m
79
+ target_string = m[1]
80
+ field_value = m.post_match
81
+ params = parse_parameters(field_value)
82
+ if params[:has_next_rel]
83
+ # To keep it simple, ignore a link with an (unlikely) anchor parameter; but the RFC
84
+ # mandates that we "MUST NOT process the link without applying the anchor", so just raise
85
+ # an exception in that (unlikely) case:
86
+ fail "anchor not supported" if params[:has_anchor]
87
+ return target_string
88
+ end
89
+ end
90
+ nil
91
+ end
92
+
93
+ def docker_search
94
+ request_params = {
95
+ method: :get,
96
+ headers: { accept: :json },
97
+ url: "#{@uri}v1/search?q=#{@search}"
98
+ }
99
+
100
+ request_params[:user] = @upstream_username if @upstream_username
101
+ request_params[:password] = @upstream_password if @upstream_password
102
+ request_params[:proxy] = proxy_uri if proxy
103
+
104
+ begin
105
+ results = RestClient::Request.execute(request_params)
106
+ JSON.parse(results)['results'].each do |result|
107
+ @found << result['name']
108
+ end
109
+ rescue
110
+ # Note: v2 endpoint does not support search
111
+ request_params[:url] = "#{@uri}v2/_catalog"
112
+ loop do
113
+ results = RestClient::Request.execute(request_params)
114
+ JSON.parse(results)['repositories'].each do |result|
115
+ @found << result
116
+ end
117
+ next_uri = get_next_link(results.headers[:link])
118
+ break if next_uri.nil?
119
+ request_params[:url] = URI(request_params[:url]).merge(next_uri).to_s
120
+ end
121
+ end
122
+ @found.sort!
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,95 @@
1
+ require 'spidr'
2
+
3
+ module Katello
4
+ module Resources
5
+ module Discovery
6
+ class Yum < RepoDiscovery
7
+ attr_reader :found, :crawled, :to_follow
8
+ def initialize(url, crawled = [], found = [], to_follow = [],
9
+ upstream_credentials_and_search = {
10
+ upstream_username: nil,
11
+ upstream_password: nil
12
+ })
13
+ @uri = uri(url)
14
+ @upstream_username = upstream_credentials_and_search[:upstream_username].presence
15
+ @upstream_password = upstream_credentials_and_search[:upstream_password].presence
16
+ @found = found
17
+ @crawled = crawled
18
+ @to_follow = to_follow
19
+ end
20
+
21
+ def run(resume_point)
22
+ if @uri.scheme == 'file'
23
+ crawl_file_path(uri(resume_point))
24
+ elsif %w(http https).include?(@uri.scheme)
25
+ spidr_crawl_pages(resume_point)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def crawl_file_path(url)
32
+ if url.path.ends_with?('/repodata/')
33
+ found_path = Pathname(url.path).parent.to_s
34
+ @found << "file://#{found_path}"
35
+ end
36
+ if url.path == @uri.path
37
+ Dir.glob("#{@uri.path}**/").each { |path| @to_follow << path }
38
+ @to_follow.shift
39
+ end
40
+ @crawled << url.path
41
+ end
42
+
43
+ def spidr_proxy_details
44
+ details = {}
45
+
46
+ if proxy
47
+ details[:host] = proxy_host
48
+ details[:port] = proxy_port
49
+ details[:user] = proxy.username
50
+ details[:password] = proxy.password
51
+ end
52
+
53
+ details
54
+ end
55
+
56
+ def spidr_crawl_pages(url)
57
+ user, password = @upstream_username, @upstream_password
58
+ Spidr.site(url, proxy: spidr_proxy_details) do |spider|
59
+ spider.authorized.add(url, user, password) if user && password
60
+ spider.every_page do |page|
61
+ page.url.query = nil
62
+ @crawled << page.url.to_s
63
+ process_page_urls(page.urls)
64
+ spider.skip_page!
65
+ end
66
+ end
67
+ end
68
+
69
+ def process_page_urls(urls)
70
+ urls.each do |url|
71
+ # Remove query parameters to avoid duplicate processing of URLs with sorting parameters etc
72
+ url.query = nil
73
+ if url.path.ends_with? 'repodata/'
74
+ @found << url.to_s.split('repodata/').first
75
+ else
76
+ @to_follow << url.to_s if should_follow?(url)
77
+ end
78
+ end
79
+ end
80
+
81
+ def should_follow?(url)
82
+ #Verify:
83
+ # * link's path includes the base url
84
+ # * link hasn't already been crawled
85
+ # * link ends with '/' so it should be a directory
86
+ # * link doesn't end with '/Packages/', as this increases
87
+ # processing time and memory usage considerably
88
+
89
+ return url.hostname == @uri.hostname && !@crawled.include?(url.to_s) &&
90
+ url.path.ends_with?('/') && !url.path.ends_with?('/Packages/')
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # Commons used helper methods pertaining to HTTP/Rest client etc
3
+ #
4
+ module Katello
5
+ module Util
6
+ module HttpHelper
7
+ def ignore_404_exception(*)
8
+ yield
9
+ rescue api.api_exception_class => e
10
+ raise e unless e.code == 404
11
+ nil
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,61 @@
1
+ From 732bd3db9f64c621c64d2be4f2a838727aac0845 Mon Sep 17 00:00:00 2001
2
+ From: Quinn James <qjames@redhat.com>
3
+ Date: Fri, 23 Jun 2023 19:06:34 +0000
4
+ Subject: [PATCH] Fixes #36530 - Fixed up
5
+ /katello/api/sync_plans/:sync_plan_id/products and
6
+ /katello/api/repositories/:repository_id/sync API endpoints.
7
+
8
+ ---
9
+ app/controllers/katello/api/v2/sync_controller.rb | 1 -
10
+ app/models/katello/repository.rb | 4 ++++
11
+ app/views/katello/api/v2/sync/index.json.rabl | 1 -
12
+ config/routes/api/v2.rb | 1 +
13
+ 4 files changed, 5 insertions(+), 2 deletions(-)
14
+ delete mode 100644 app/views/katello/api/v2/sync/index.json.rabl
15
+
16
+ diff --git a/app/controllers/katello/api/v2/sync_controller.rb b/app/controllers/katello/api/v2/sync_controller.rb
17
+ index b43d502264f..cf324a004f6 100644
18
+ --- a/app/controllers/katello/api/v2/sync_controller.rb
19
+ +++ b/app/controllers/katello/api/v2/sync_controller.rb
20
+ @@ -4,7 +4,6 @@ class Api::V2::SyncController < Api::V2::ApiController
21
+ before_action :find_object, :only => [:index]
22
+ before_action :ensure_library, :only => [:index]
23
+
24
+ - api :GET, "/organizations/:organization_id/products/:product_id/sync", N_("Get status of repo synchronisation for given product")
25
+ api :GET, "/repositories/:repository_id/sync", N_("Get status of synchronisation for given repository")
26
+ def index
27
+ respond_for_async(:resource => @obj.sync_status)
28
+ diff --git a/app/models/katello/repository.rb b/app/models/katello/repository.rb
29
+ index 1fbf499ec44..ccd76734ba6 100644
30
+ --- a/app/models/katello/repository.rb
31
+ +++ b/app/models/katello/repository.rb
32
+ @@ -947,6 +947,10 @@ def in_content_view?(content_view)
33
+ content_view.repositories.include? self
34
+ end
35
+
36
+ + def sync_status
37
+ + return latest_dynflow_sync
38
+ + end
39
+ +
40
+ protected
41
+
42
+ def unit_type_for_removal(type_class = nil)
43
+ diff --git a/app/views/katello/api/v2/sync/index.json.rabl b/app/views/katello/api/v2/sync/index.json.rabl
44
+ deleted file mode 100644
45
+ index 4296aca962b..00000000000
46
+ --- a/app/views/katello/api/v2/sync/index.json.rabl
47
+ +++ /dev/null
48
+ @@ -1 +0,0 @@
49
+ -extends 'katello/api/v2/common/async'
50
+ diff --git a/config/routes/api/v2.rb b/config/routes/api/v2.rb
51
+ index 67ad91dbc2c..9ec5312b4e0 100644
52
+ --- a/config/routes/api/v2.rb
53
+ +++ b/config/routes/api/v2.rb
54
+ @@ -484,6 +484,7 @@ class ActionDispatch::Routing::Mapper
55
+ end
56
+
57
+ api_resources :sync_plans, :only => [:index, :show, :update, :destroy] do
58
+ + api_resources :products, :only => [:index]
59
+ get :auto_complete_search, :on => :collection
60
+ put :sync
61
+ end
@@ -454,6 +454,8 @@ module Katello
454
454
  ).each do |facet|
455
455
  facet.update_applicability_counts
456
456
  facet.update_errata_status
457
+ rescue NoMethodError
458
+ Rails.logger.warn _('Errata statuses not updated for deleted content facet with UUID %s') % facet.uuid
457
459
  end
458
460
  end
459
461
  end
@@ -133,7 +133,14 @@ module Katello
133
133
 
134
134
  repo_param[:mirroring_policy] = Katello::RootRepository::MIRRORING_POLICY_ADDITIVE if repo_param[:mirroring_policy].blank?
135
135
 
136
- RootRepository.new(repo_param.merge(:product_id => self.id))
136
+ repo_param = repo_param.merge(:product_id => self.id)
137
+
138
+ # Container push may concurrently call root add several times before the db can update.
139
+ if repo_param[:is_container_push]
140
+ RootRepository.create_or_find_by!(repo_param)
141
+ else
142
+ RootRepository.new(repo_param)
143
+ end
137
144
  end
138
145
  end
139
146
  end
@@ -118,7 +118,7 @@ module Katello
118
118
  end}
119
119
 
120
120
  before_validation :set_pulp_id
121
- before_validation :set_container_repository_name, :if => :docker?
121
+ before_validation :set_container_repository_name, :unless => :skip_container_name?
122
122
 
123
123
  scope :has_url, -> { joins(:root).where.not("#{RootRepository.table_name}.url" => nil) }
124
124
  scope :on_demand, -> { joins(:root).where("#{RootRepository.table_name}.download_policy" => ::Katello::RootRepository::DOWNLOAD_ON_DEMAND) }
@@ -268,6 +268,10 @@ module Katello
268
268
  self.content_view_version.content_view
269
269
  end
270
270
 
271
+ def skip_container_name?
272
+ self.library_instance? && self.root.docker? && self.root.is_container_push
273
+ end
274
+
271
275
  def library_instance?
272
276
  self.content_view.default?
273
277
  end