foreman_scc_manager 1.8.19 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -10
  3. data/app/controllers/api/v2/scc_accounts_controller.rb +48 -5
  4. data/app/controllers/api/v2/scc_products_controller.rb +1 -1
  5. data/app/controllers/scc_accounts_controller.rb +47 -0
  6. data/app/lib/actions/scc_manager/subscribe_product.rb +61 -19
  7. data/app/models/scc_account.rb +4 -3
  8. data/app/models/scc_katello_repository.rb +4 -0
  9. data/app/models/scc_product.rb +7 -1
  10. data/app/models/scc_repository.rb +12 -4
  11. data/app/views/scc_accounts/show.html.erb +7 -51
  12. data/config/routes.rb +1 -0
  13. data/db/migrate/20220425132605_add_scc_katello_repository_relation.rb +10 -0
  14. data/db/migrate/20220429100843_remove_root_repository_id_from_scc_repository.rb +6 -0
  15. data/db/migrate/20220429102717_populate_scc_katello_repositories.rb +21 -0
  16. data/db/migrate/20220513132652_populate_upstream_authentication_token.rb +23 -0
  17. data/db/migrate/20220531120722_add_product_category_to_scc_products.rb +5 -0
  18. data/lib/foreman_scc_manager/engine.rb +1 -1
  19. data/lib/foreman_scc_manager/version.rb +1 -1
  20. data/lib/tasks/republish_repositories.rake +13 -0
  21. data/lib/tasks/setup_authentication_token.rake +27 -0
  22. data/package.json +54 -0
  23. data/test/controllers/scc_accounts_controller_test.rb +6 -0
  24. data/test/fixtures/models/katello_root_repositories.yml +44 -0
  25. data/test/fixtures/models/scc_repositories.yml +34 -0
  26. data/test/support/fixtures_support.rb +3 -1
  27. data/webpack/components/SCCProductPage/EmptySccProducts.js +46 -0
  28. data/webpack/components/SCCProductPage/SCCProductPage.js +96 -0
  29. data/webpack/components/SCCProductPage/SCCProductPageActions.js +27 -0
  30. data/webpack/components/SCCProductPage/SCCProductPageConstants.js +5 -0
  31. data/webpack/components/SCCProductPage/SCCProductPageReducer.js +34 -0
  32. data/webpack/components/SCCProductPage/SCCProductPageSelectors.js +7 -0
  33. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCGenericPicker/index.js +80 -0
  34. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/index.js +174 -0
  35. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/styles.scss +3 -0
  36. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/index.js +303 -0
  37. data/webpack/components/SCCProductPage/components/SCCProductPicker/index.js +235 -0
  38. data/webpack/components/SCCProductPage/components/SCCProductPicker/styles.scss +10 -0
  39. data/webpack/components/SCCProductPage/components/SCCProductPickerModal/index.js +81 -0
  40. data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/index.js +113 -0
  41. data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/styles.scss +14 -0
  42. data/webpack/components/SCCProductPage/components/SCCProductView/index.js +236 -0
  43. data/webpack/components/SCCProductPage/components/common/SCCGenericExpander/index.js +58 -0
  44. data/webpack/components/SCCProductPage/components/common/SCCProductTreeExpander/index.js +21 -0
  45. data/webpack/components/SCCProductPage/components/common/SCCSubscribedProductsExpander/index.js +21 -0
  46. data/webpack/components/SCCProductPage/index.js +18 -0
  47. data/webpack/components/SCCProductPage/sccProductPage.scss +8 -0
  48. data/webpack/index.js +11 -0
  49. data/webpack/reducer.js +7 -0
  50. metadata +48 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94924eef8de5143907ca81d9ea6c41915d51c931d394356497e7168b8ffbe09e
4
- data.tar.gz: 49d822f0d3c70abd53a0d818c3dc7c2102c9e12e8c325a3580f9e966bf50dd61
3
+ metadata.gz: 73d2510abbdbe51fef59bba613502fb02ef1fe14eeb441f0bee716b17e2eba8d
4
+ data.tar.gz: c61b9f852d58033c290204508d0d4653e5379c6c8b73e6d5b58d026ea485b0f0
5
5
  SHA512:
6
- metadata.gz: c26c8d840bd3a97a3cbc99e84dc53bc17c48a8175dd026cfd618d5597cbf934373ed048c978e5805db1942ef0b3b4ef9283ee60b8e36b26e5d3a886e48f20067
7
- data.tar.gz: 6599cad1358b63afba7824ca2d8f7328d30c8f2d851814b681be2b7ec4de42116e9e3160680cd814f960cb49b65fd09edda863a7383e5fabd93925791bd6600e
6
+ metadata.gz: d3ce92fbe191d0aaa6f10d0015ed4a3e3320892d1f45a519a965cdbd63a93e49a0fe6918fcc1b7cfb4618b8a8fc016349bfbceba5825a93ca5400dce5dd18e03
7
+ data.tar.gz: a48593aa198df8fbc5b4c88a2654facb7dc1f77f80373d3cf7a03fa46e84bd5f02af338af0a0e1dd2968da7e7afa6e44548f992e51d1ece7a0a82fa05922f0d9
data/README.md CHANGED
@@ -8,7 +8,13 @@ Foreman plugin to sync SUSE Customer Center products and repositories into Katel
8
8
 
9
9
  ## Installation
10
10
 
11
- This plugin installation is not supported by foreman-installer and has to be installed manually:
11
+ This plugin installation is supported since Foreman 3.4 / Katello 4.6 by the foreman-installer, but only with the scenario Katello:
12
+
13
+ ```sh
14
+ foreman-install --scenario katello --enable-foreman-plugin-scc-manager
15
+ ```
16
+
17
+ You can also install it manually:
12
18
 
13
19
  ```sh
14
20
  yum install tfm-rubygem-foreman_scc_manager
@@ -20,18 +26,16 @@ foreman-installer
20
26
 
21
27
  | Foreman Version | Katello Version | Plugin Version |
22
28
  | --------------- | --------------- | -------------- |
29
+ | 3.5 | 4.5 | ~> 2.0.0 |
30
+ | 3.1 | 4.3 | ~> 1.8.20\* |
31
+ | 3.0 | 4.2 | ~> 1.8.20 |
32
+ | 2.5 | 4.1 | ~> 1.8.20 |
23
33
  | 2.3 | 3.18 | ~> 1.8.9 |
24
34
  | 2.1 | 3.16 | ~> 1.8.5 |
25
35
  | 2.0 | 3.16 | ~> 1.8.4 |
26
36
  | 1.24 | 3.14 | ~> 1.8.0 |
27
- | 1.22 | 3.12 | ~> 1.7.0 |
28
- | 1.21 | 3.10 | ~> 1.6.0 |
29
- | 1.20 | 3.9 | ~> 1.6.0 |
30
- | 1.19 | 3.8 | ~> 1.5.1 |
31
- | 1.18 | 3.7 | ~> 1.5.0 |
32
- | 1.17 | 3.6 | >= 1.3.1 |
33
- | 1.16 | 3.5 | <= 1.3.0 |
34
- | 1.15 | 3.4 | ~> 1.1.0 |
37
+
38
+ \* If you are using foreman_scc_manager in version 1.8.20 and then upgrade to Katello 4.3, you need to manually run the following rake task on your Foreman instance: `foreman-rake foreman_scc_manager:setup_authentication_tokens`.
35
39
 
36
40
  ## Documentation
37
41
 
@@ -47,7 +51,7 @@ Fork and send a Pull Request. Thanks!
47
51
 
48
52
  ## Copyright
49
53
 
50
- Copyright (c) 2021 ATIX AG - https://atix.de
54
+ Copyright (c) 2022 ATIX AG - https://atix.de
51
55
 
52
56
  This program is free software: you can redistribute it and/or modify
53
57
  it under the terms of the GNU General Public License as published by
@@ -10,7 +10,7 @@ module Api
10
10
  api_base_url '/api/v2'
11
11
  end
12
12
 
13
- before_action :find_resource, :only => [:show, :update, :destroy, :sync, :bulk_subscribe]
13
+ before_action :find_resource, :only => [:show, :update, :destroy, :sync, :bulk_subscribe, :bulk_subscribe_with_repos]
14
14
 
15
15
  api :GET, '/scc_accounts/', N_('List all scc_accounts')
16
16
  param :organization_id, :identifier, :required => true
@@ -33,7 +33,7 @@ module Api
33
33
  param :login, String, :required => true, :desc => N_('Login id of scc_account')
34
34
  param :password, String, :required => true, :desc => N_('Password of scc_account')
35
35
  param :base_url, String, :required => false, :desc => N_('URL of SUSE for scc_account')
36
- param :interval, ['never', 'daily', 'weekly', 'monthy'], :desc => N_('Interval for syncing scc_account')
36
+ param :interval, ['never', 'daily', 'weekly', 'monthly'], :desc => N_('Interval for syncing scc_account')
37
37
  param :sync_date, String, :desc => N_('Date and time relative to which the sync interval is run')
38
38
  param :katello_gpg_key_id, :identifier, :required => false, :desc => N_('Associated GPG key of scc_account')
39
39
  end
@@ -105,12 +105,16 @@ module Api
105
105
  param :id, :identifier_dottable, :required => true
106
106
  param :scc_subscribe_product_ids, Array, :required => true
107
107
  def bulk_subscribe
108
- scc_products_to_subscribe = @scc_account.scc_products.where(:id => params[:scc_subscribe_product_ids])
109
108
  respond_to do |format|
110
- if scc_products_to_subscribe.count > 0
109
+ if params[:scc_subscribe_product_ids].count > 0
110
+ # we need to pass two parameters to the product subscribe task,
111
+ # the product itself and an array of repo ids
112
+ # if the id array is empty, all repositories will be subscribed to
113
+ scc_products = @scc_account.scc_products.where(:id => params[:scc_subscribe_product_ids])
111
114
  subscribe_task = ForemanTasks.async_task(::Actions::BulkAction,
112
115
  ::Actions::SccManager::SubscribeProduct,
113
- scc_products_to_subscribe)
116
+ scc_products,
117
+ {})
114
118
  format.json { render json: subscribe_task.to_json, status: :ok }
115
119
  else
116
120
  format.json { render json: { error: 'No Product selected' }, status: :expectation_failed }
@@ -122,6 +126,43 @@ module Api
122
126
  render json: { error: ('Lock on SCC account already taken: %s' % e).to_s }, status: :unprocessable_entity
123
127
  end
124
128
 
129
+ def_param_group :scc_product_data do
130
+ param :scc_product_data, Array, :required => true, :desc => 'Array of Hash elements. One hash element contains an scc_product_id and a repository_list.' do
131
+ param :scc_product_id, Integer, :required => true, :desc => 'Product ID of SCC product'
132
+ param :repository_list, Array, of: Integer, :required => false,
133
+ :desc => 'List of SCC repositories belonging to the SCC product. If the list is empty, all repositories will be subscribed to.'
134
+ end
135
+ end
136
+
137
+ api :PUT, '/scc_accounts/:id/bulk_subscribe_with_repos/', N_('Bulk subscription of scc_products with individual repository selection for scc_account.')
138
+ param :id, :identifier_dottable, :required => true
139
+ param_group :scc_product_data
140
+ def bulk_subscribe_with_repos
141
+ respond_to do |format|
142
+ # if we want to subscribe to specific repos, we need to pass the product and the
143
+ # corresponding repository ids instead of scc products only
144
+ if params[:scc_product_data].count > 0
145
+ scc_products = @scc_account.scc_products.where(:id => params[:scc_product_data].pluck(:scc_product_id))
146
+ if scc_products.empty?
147
+ format.json { render json: { error: _('The selected products cannot be found for this SCC account.') }, status: :unprocessable_entity }
148
+ else
149
+ action_args = params[:scc_product_data].map { |p| { p['scc_product_id'] => p['repository_list'] } }.inject(:merge)
150
+ subscribe_task = ForemanTasks.async_task(::Actions::BulkAction,
151
+ ::Actions::SccManager::SubscribeProduct,
152
+ scc_products,
153
+ action_args)
154
+ format.json { render json: subscribe_task.to_json, status: :ok }
155
+ end
156
+ else
157
+ format.json { render json: { error: 'No Product selected' }, status: :expectation_failed }
158
+ end
159
+ end
160
+ rescue ::Foreman::Exception => e
161
+ render json: { error: ('Failed to add task to queue: %s' % e).to_s }, status: :unprocessable_entity
162
+ rescue ForemanTasks::Lock::LockConflict => e
163
+ render json: { error: ('Lock on SCC account already taken: %s' % e).to_s }, status: :unprocessable_entity
164
+ end
165
+
125
166
  private
126
167
 
127
168
  def scc_account_params
@@ -152,6 +193,8 @@ module Api
152
193
  :sync
153
194
  when 'bulk_subscribe'
154
195
  :bulk_subscribe
196
+ when 'bulk_subscribe_with_repos'
197
+ :bulk_subscribe_with_repos
155
198
  else
156
199
  super
157
200
  end
@@ -41,7 +41,7 @@ module Api
41
41
  param :id, :identifier_dottable, :required => true
42
42
  param :scc_account_id, :identifier_dottable, :required => true
43
43
  def subscribe
44
- subcribe_task = ForemanTasks.async_task(::Actions::SccManager::SubscribeProduct, @scc_product) if @scc_product
44
+ subcribe_task = ForemanTasks.async_task(::Actions::SccManager::SubscribeProduct, @scc_product, {}) if @scc_product
45
45
  respond_to do |format|
46
46
  if subcribe_task
47
47
  format.json { render json: subcribe_task.to_json, status: :ok }
@@ -1,5 +1,6 @@
1
1
  class SccAccountsController < ApplicationController
2
2
  helper_method :scc_filtered_products
3
+ helper_method :create_nested_product_tree
3
4
  before_action :find_organization
4
5
  before_action :find_resource, only: %i[show edit update destroy sync bulk_subscribe]
5
6
  before_action :find_available_gpg_keys, only: %i[new edit update create]
@@ -97,6 +98,52 @@ class SccAccountsController < ApplicationController
97
98
  redirect_to scc_accounts_path
98
99
  end
99
100
 
101
+ def create_nested_product_tree(scc_products, subscribed_only:)
102
+ if subscribed_only
103
+ scc_products_base = scc_products.where(:product_type => 'base').where.not(:product_id => nil).includes([:scc_extensions])
104
+ else
105
+ scc_products_base = scc_products.where(:product_type => 'base').includes([:scc_extensions])
106
+ end
107
+ tree = []
108
+ scc_products_base.each do |p|
109
+ tree.push(get_product_tree_hash(p))
110
+ end
111
+
112
+ tree
113
+ end
114
+
115
+ def scc_product_hash(scc_product)
116
+ scc_product_json = scc_product.as_json(:only => [:scc_id, :id, :arch, :version, :product_id, :subscription_valid],
117
+ include: { :scc_repositories => { :only => [:id, :name, :subscription_valid] } })
118
+ .merge('name' => scc_product.pretty_name, 'product_category' => scc_product.product_category)
119
+ # find if and which Katello root repository is associated with this SCC product
120
+ repo_ids_katello = scc_product.product.blank? || scc_product.product.root_repository_ids.blank? ? nil : scc_product.product.root_repository_ids
121
+ scc_product_json['scc_repositories'].each do |repo|
122
+ # byebug
123
+ if repo_ids_katello.blank?
124
+ repo['katello_root_repository_id'] = nil
125
+ else
126
+ repo_ids_scc = scc_product.scc_repositories.find(repo['id']).katello_root_repository_ids
127
+ repo['katello_root_repository_id'] = repo_ids_scc.blank? ? nil : (repo_ids_scc & repo_ids_katello).first
128
+ end
129
+ end
130
+ scc_product_json
131
+ end
132
+
133
+ def get_product_tree_hash(scc_product_base)
134
+ if scc_product_base.scc_extensions.blank?
135
+ scc_product_hash(scc_product_base)
136
+ else
137
+ children = []
138
+ scc_product_base.scc_extensions.each do |ext|
139
+ children.push(get_product_tree_hash(ext))
140
+ end
141
+ scc_product_base_hash = scc_product_hash(scc_product_base)
142
+ scc_product_base_hash['children'] = children
143
+ scc_product_base_hash
144
+ end
145
+ end
146
+
100
147
  private
101
148
 
102
149
  def find_available_gpg_keys
@@ -1,44 +1,80 @@
1
1
  module Actions
2
2
  module SccManager
3
3
  class SubscribeProduct < Actions::EntryAction
4
- def plan(scc_product)
5
- raise _('Product already subscribed!') if scc_product.product
4
+ # scc_product is an ActiveRecord object of Class SccProduct
5
+ # scc_repos_to_subscribe is a hash with the product id as keys and an array of
6
+ # repos to subscribe as values
7
+ # rubocop:disable Metrics/MethodLength
8
+ def plan(scc_product, scc_repos_to_subscribe)
9
+ if scc_product.product
10
+ ::Foreman::Logging.logger('foreman_scc_manager')
11
+ .info("SccProduct '#{scc_product.friendly_name}' is already subscribed to.")
12
+ else
13
+ ::Foreman::Logging.logger('foreman_scc_manager')
14
+ .info("Initiating subscription for SccProduct '#{scc_product.friendly_name}'.")
15
+ end
6
16
 
7
- ::Foreman::Logging.logger('foreman_scc_manager')
8
- .info("Initiating subscription for SccProduct '#{scc_product.friendly_name}'.")
9
17
  sequence do
10
- product_create_action = plan_action(CreateProduct,
11
- :product_name => scc_product.pretty_name,
12
- :product_description => scc_product.pretty_description,
13
- :organization_id => scc_product.organization.id,
14
- :gpg_key => scc_product.scc_account.katello_gpg_key_id)
18
+ if scc_product.product_id.nil?
19
+ product_create_action = plan_action(CreateProduct,
20
+ :product_name => scc_product.pretty_name,
21
+ :product_description => scc_product.pretty_description,
22
+ :organization_id => scc_product.organization.id,
23
+ :gpg_key => scc_product.scc_account.katello_gpg_key_id)
24
+ end
15
25
  katello_repos = {}
16
- scc_product.scc_repositories.each do |repo|
26
+ # we need to set the repositories to subscribe to
27
+ scc_repos = []
28
+ if scc_repos_to_subscribe.empty?
29
+ scc_repos = scc_product.scc_repositories
30
+ else
31
+ # at this point, we need to make sure that the repository list is valid
32
+ # we want to subscribe only to repos that we have not subscribed before
33
+ repo_ids_katello = scc_product.product.blank? || scc_product.product.root_repository_ids.blank? ? nil : scc_product.product.root_repository_ids
34
+ scc_repos = scc_product.scc_repositories.where(id: scc_repos_to_subscribe[scc_product.id])
35
+ unless repo_ids_katello.nil? || scc_repos.empty?
36
+ # remove repo if Katello repo is already associated
37
+ scc_repos.reject { |repo| (repo.katello_root_repositories & repo_ids_katello).present? }
38
+ end
39
+ end
40
+ if scc_repos.empty?
41
+ ::Foreman::Logging.logger('foreman_scc_manager')
42
+ .info('The repositories you have selected are either already subscribed to or invalid.')
43
+ else
44
+ ::Foreman::Logging.logger('foreman_scc_manager')
45
+ .info("Subscribing to SCC repositories '#{scc_repos.pluck(:id)}'.
46
+ If you requested more repositories, please check if those are already subscribed to or invalid.")
47
+ end
48
+ scc_repos.each do |repo|
17
49
  arch = scc_product.arch || 'noarch'
18
50
  repo_create_action = plan_action(CreateRepository,
19
- :product_id => product_create_action.output[:product_id],
51
+ :product_id => scc_product.product_id || product_create_action.output[:product_id],
20
52
  :uniq_name => repo.uniq_name(scc_product),
21
53
  :pretty_repo_name => repo.pretty_name,
22
- :url => repo.full_url,
54
+ :url => repo.url,
55
+ :token => repo.token,
23
56
  :arch => arch)
24
57
  katello_repos[repo.id] = repo_create_action.output[:katello_root_repository_id]
25
58
  end
59
+
26
60
  # connect action to resource (=> make parameters accessable in input)
27
- action_subject(scc_product, product_id: product_create_action.output[:product_id])
61
+ action_subject(scc_product, product_id: product_create_action.output[:product_id]) if scc_product.product_id.nil?
28
62
  input.update(katello_repos: katello_repos)
29
63
  plan_self
30
64
  end
31
65
  end
66
+ # rubocop:enable Metrics/MethodLength
32
67
 
33
68
  def finalize
34
69
  # connect Scc products and Katello products
35
- scc_product = SccProduct.find(input[:scc_product][:id])
36
- product = ::Katello::Product.find(input[:product_id])
37
- scc_product.update!(product: product)
70
+ # it may happen that we only append repos to an existing product
71
+ unless input[:scc_product].nil?
72
+ scc_product = SccProduct.find(input[:scc_product][:id])
73
+ scc_product.update!(product_id: input[:product_id])
74
+ end
38
75
  # extract Katello repo ids from input hash and store to database
39
76
  input[:katello_repos].each do |scc_repo_id, katello_root_repository_id|
40
- scc_repo = SccRepository.find(scc_repo_id)
41
- scc_repo.update!(katello_root_repository_id: katello_root_repository_id)
77
+ SccKatelloRepository.find_or_create_by(scc_repository_id: scc_repo_id, katello_root_repository_id: katello_root_repository_id)
42
78
  end
43
79
  end
44
80
 
@@ -79,7 +115,6 @@ module Actions
79
115
  gpg_key = product.gpg_key
80
116
  repo_param = { :label => label,
81
117
  :name => input[:pretty_repo_name],
82
- :url => input[:url],
83
118
  :content_type => 'yum',
84
119
  :unprotected => unprotected,
85
120
  :gpg_key => gpg_key,
@@ -91,6 +126,13 @@ module Actions
91
126
  else
92
127
  repository.mirroring_policy = ::Katello::RootRepository::MIRRORING_POLICY_CONTENT
93
128
  end
129
+ if repository.has_attribute?('upstream_authentication_token')
130
+ repository.url = input[:url]
131
+ repository.upstream_authentication_token = input[:token]
132
+ else
133
+ repository.url = input[:token].blank? ? input[:url] : "#{input[:url]}?#{input[:token]}"
134
+ end
135
+
94
136
  repository.verify_ssl_on_sync = true
95
137
  trigger(::Actions::Katello::Repository::CreateRoot, repository).tap do
96
138
  output[:katello_root_repository_id] = repository.id
@@ -194,12 +194,13 @@ class SccAccount < ApplicationRecord
194
194
 
195
195
  # all scc repos that are kept but not available upstream need to be marked invalid
196
196
  # subscription_valid can be set to nil
197
- to_invalidate = invalidated_repos.select { |ir| ir.katello_root_repository_id.present? && !ir.subscription_valid }
197
+ to_invalidate = invalidated_repos.select { |ir| ir.katello_root_repositories.count > 0 && !ir.subscription_valid }
198
198
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Invalidating #{to_invalidate.count} expired repositories"
199
199
  invalidate_subscription_status(to_invalidate, save_record: true)
200
200
 
201
201
  # delete repositories being removed upstream and that are not subscribed to
202
- to_delete = scc_repositories.where.not(scc_id: upstream_repo_ids)
202
+ to_delete = scc_repositories.where.not(scc_id: upstream_repo_ids).includes(:scc_katello_repositories).where(scc_katello_repositories: { id: nil })
203
+
203
204
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Deleting #{to_delete.count} old repositories"
204
205
  to_delete.destroy_all
205
206
  end
@@ -218,8 +219,8 @@ class SccAccount < ApplicationRecord
218
219
  cached_product.product_type = up['product_type']
219
220
  cached_product.scc_repositories =
220
221
  scc_repositories.where(scc_id: up['repositories'].map { |repo| repo['id'] })
221
- # name should be set after friendly_name because it depends on friendly_name
222
222
  cached_product.name = cached_product.pretty_name
223
+ cached_product.product_category = up['name']
223
224
  cached_product.description = cached_product.pretty_description
224
225
  cached_product.subscription_valid = true
225
226
  cached_product.save!
@@ -0,0 +1,4 @@
1
+ class SccKatelloRepository < ApplicationRecord
2
+ belongs_to :scc_repository
3
+ belongs_to :katello_root_repository, class_name: 'Katello::RootRepository'
4
+ end
@@ -56,13 +56,19 @@ class SccProduct < ApplicationRecord
56
56
  label = Katello::Util::Model.labelize(uniq_repo_name)
57
57
  unprotected = true
58
58
  gpg_key = new_product.gpg_key
59
- new_repo = new_product.add_repo(label, uniq_repo_name, repo.full_url, 'yum', unprotected, gpg_key)
59
+ new_repo = new_product.add_repo(label, uniq_repo_name, repo.url, 'yum', unprotected, gpg_key)
60
60
  new_repo.arch = arch || 'noarch'
61
61
  if new_repo.has_attribute?('mirror_on_sync')
62
62
  new_repo.mirror_on_sync = true
63
63
  else
64
64
  new_repo.mirroring_policy = ::Katello::RootRepository::MIRRORING_POLICY_CONTENT
65
65
  end
66
+ if new_repo.has_attribute?('upstream_authentication_token')
67
+ new_repo.upstream_authentication_token = repo.token
68
+ new_repo.url = repo.url
69
+ else
70
+ new_repo.url = repo.full_url
71
+ end
66
72
  new_repo.verify_ssl_on_sync = true
67
73
  ForemanTasks.sync_task(::Actions::Katello::Repository::Create, new_repo, false, false)
68
74
  end
@@ -4,7 +4,8 @@ class SccRepository < ApplicationRecord
4
4
  self.include_root_in_json = false
5
5
 
6
6
  belongs_to :scc_account
7
- belongs_to :katello_root_repository, class_name: 'Katello::RootRepository'
7
+ has_many :scc_katello_repositories
8
+ has_many :katello_root_repositories, through: :scc_katello_repositories
8
9
  has_one :organization, through: :scc_account
9
10
  has_and_belongs_to_many :scc_products
10
11
 
@@ -27,9 +28,16 @@ class SccRepository < ApplicationRecord
27
28
  # as uniq_name was changed, we need to look for repo labels that end with the repo name
28
29
  katello_repos = Katello::RootRepository.where('label like ?', "%#{::Katello::Util::Model.labelize(self.description)}%")
29
30
  katello_repos.each do |repo|
30
- unless repo.url == full_url
31
- ::Foreman::Logging.logger('foreman_scc_manager').info "Update URL-token for repository '#{repo.name}'."
32
- ForemanTasks.async_task(::Actions::Katello::Repository::Update, repo, url: full_url)
31
+ if repo.has_attribute?('upstream_authentication_token')
32
+ unless repo.url == url && repo.upstream_authentication_token == token
33
+ ForemanTasks.async_task(::Actions::Katello::Repository::Update, repo, url: url, upstream_authentication_token: token)
34
+ ::Foreman::Logging.logger('foreman_scc_manager').info "Update URL and authentication token for repository '#{repo.name}'."
35
+ end
36
+ else
37
+ unless repo.url == full_url
38
+ ForemanTasks.async_task(::Actions::Katello::Repository::Update, repo, url: full_url)
39
+ ::Foreman::Logging.logger('foreman_scc_manager').info "Update URL-token for repository '#{repo.name}'."
40
+ end
33
41
  end
34
42
  end
35
43
  end
@@ -1,52 +1,8 @@
1
1
  <% title (_("Product Selection for Account %s") % @scc_account) %>
2
-
3
- <% javascript 'foreman_scc_manager/scc_accounts' %>
4
- <%= form_for([:bulk_subscribe, @scc_account], method: :put) do |f| %>
5
- <% def render_list_node(f, scc_product, parent_id = "") %>
6
- <li>
7
- <span class="scc_product_checkbox" id="<%= "scc_product_span_#{parent_id + "_" + scc_product.id.to_s}" %>" <%= "data-parent=scc_product_span_#{parent_id}" if parent_id != "" %>>
8
- <% if scc_product.product %>
9
- <%= check_box_tag("scc_account[scc_unsubscribe_product_ids][]", scc_product.id, true, disabled: true) %>
10
- <%= link_to(scc_product.pretty_name, "/products/#{scc_product.product_id}") %>
11
- <% else %>
12
- <%= check_box_tag("scc_account[scc_subscribe_product_ids][]", scc_product.id, false) %>
13
- <%= scc_product.pretty_name %>
14
- <% end %>
15
- <% unless scc_product.subscription_valid? %>
16
- <span style="color:red">
17
- <%= _('(WARNING: Please check your SUSE subscription)') %>
18
- </span>
19
- <% end %>
20
- </span>
21
- <% if scc_product.scc_extensions.any? %>
22
- <ul style='padding-left: 20px;'>
23
- <% scc_filtered_products(scc_product.scc_extensions, 'extension').each do |scc_extension| %>
24
- <% render_list_node(f, scc_extension, parent_id + "_" + scc_product.id.to_s) %>
25
- <% end %>
26
- </ul>
27
- <% end %>
28
- </li>
29
- <% end %>
30
-
31
- <%= f.hidden_field :prevent_missing, value: 1 %>
32
- <ul class="nav nav-tabs" data-tabs="tabs">
33
- <li class="active"><a href="#primary" data-toggle="tab"><%= _("SUSE Customer Center") %></a></li>
34
- </ul>
35
-
36
- <div class="tab-content">
37
- <div class="tab-pane active" id="primary">
38
- <ul>
39
- <% if @scc_account.synced %>
40
- <% scc_filtered_products(@scc_account.scc_products).each do |scc_product| %>
41
- <% render_list_node(f, scc_product) %>
42
- <% end %>
43
- <% else %>
44
- <%= _('Please sync your SUSE subscriptions first.') %>
45
- <% submit_disabled = true %>
46
- <% end %>
47
- </ul>
48
- </div>
49
- <%= submit_or_cancel f, false, {disabled: submit_disabled || false} %>
50
- </div>
51
- <% end %>
52
-
2
+ <%= webpacked_plugins_js_for :foreman_scc_manager %>
3
+ <%= webpacked_plugins_css_for :foreman_scc_manager %>
4
+ <%= react_component('SCCProductPage', {
5
+ :canCreate => authorized_for(action: :sync),
6
+ :sccAccountId => @scc_account.id,
7
+ :sccProductsInit => create_nested_product_tree(@scc_account.scc_products, subscribed_only: false)
8
+ }) %>
data/config/routes.rb CHANGED
@@ -32,6 +32,7 @@ Rails.application.routes.draw do
32
32
  put 'test_connection'
33
33
  put 'sync'
34
34
  put 'bulk_subscribe'
35
+ put 'bulk_subscribe_with_repos'
35
36
  end
36
37
  end
37
38
  constraints(:scc_account_id => /[^\/]+/) do
@@ -0,0 +1,10 @@
1
+ class AddSccKatelloRepositoryRelation < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table(:scc_katello_repositories) do |t|
4
+ t.references :scc_repository, null: false, foreign_key: { on_delete: :cascade }
5
+ t.references :katello_root_repository, null: false, foreign_key: { on_delete: :cascade }
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ class RemoveRootRepositoryIdFromSccRepository < ActiveRecord::Migration[6.0]
2
+ def change
3
+ remove_foreign_key :scc_repositories, :katello_root_repositories, column: :katello_root_repository_id, on_delete: :nullify
4
+ remove_column :scc_repositories, :katello_root_repository_id, :integer, index: true, null: true
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ class PopulateSccKatelloRepositories < ActiveRecord::Migration[6.0]
2
+ class SccProduct < ApplicationRecord
3
+ belongs_to :product, class_name: 'Katello::Product'
4
+ has_and_belongs_to_many :scc_repositories
5
+ end
6
+ class SccRepository < ApplicationRecord
7
+ has_many :scc_katello_repositories
8
+ has_many :katello_root_repositories, through: :scc_katello_repositories
9
+ has_and_belongs_to_many :scc_products
10
+ end
11
+
12
+ def up
13
+ SccProduct.where.not(product_id: nil).each do |scc_p|
14
+ scc_p.scc_repositories.each do |scc_r|
15
+ # find all Katello repositories that were derived from that SCC repository
16
+ katello_repos = Katello::RootRepository.where('label like ?', "%#{::Katello::Util::Model.labelize(scc_r.description)}%")
17
+ scc_r.update!(katello_root_repositories: katello_repos)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ class PopulateUpstreamAuthenticationToken < ActiveRecord::Migration[6.0]
2
+ def up
3
+ return unless ActiveRecord::Base.connection.column_exists?('katello_root_repositories', 'upstream_authentication_token')
4
+
5
+ scc_repositories = SccRepository.includes(:scc_katello_repositories).where.not(scc_katello_repositories: { id: nil })
6
+ scc_repositories.each do |scc_repo|
7
+ scc_repo.katello_root_repositories.each do |katello_repo|
8
+ katello_repo.update!(url: scc_repo.url, upstream_authentication_token: scc_repo.token)
9
+ end
10
+ end
11
+ end
12
+
13
+ def down
14
+ return if ActiveRecord::Base.connection.column_exists?('katello_root_repositories', 'upstream_authentication_token')
15
+
16
+ scc_repositories = SccRepository.includes(:scc_katello_repositories).where.not(scc_katello_repositories: { id: nil })
17
+ scc_repositories.each do |scc_repo|
18
+ scc_repo.katello_root_repositories.each do |katello_repo|
19
+ katello_repo.update!(url: scc_repo.full_url)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ class AddProductCategoryToSccProducts < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :scc_products, :product_category, :string, null: true
4
+ end
5
+ end
@@ -38,7 +38,7 @@ module ForemanSccManager
38
38
 
39
39
  permission :use_scc_accounts,
40
40
  { :scc_accounts => [:bulk_subscribe],
41
- :'api/v2/scc_accounts' => [:bulk_subscribe] },
41
+ :'api/v2/scc_accounts' => [:bulk_subscribe, :bulk_subscribe_with_repos] },
42
42
  :resource_type => 'SccAccount'
43
43
 
44
44
  permission :new_scc_accounts,
@@ -1,3 +1,3 @@
1
1
  module ForemanSccManager
2
- VERSION = '1.8.19'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -0,0 +1,13 @@
1
+ namespace :foreman_scc_manager do
2
+ desc 'Republish all SCC-repositories.'
3
+ task :republish_scc_repositories => ['dynflow:client', 'katello:check_ping'] do
4
+ needing_publish = SccKatelloRepository.joins(:katello_root_repository)
5
+ .joins(:katello_root_repository => :repositories)
6
+ .pluck("#{Katello::Repository.table_name}.id")
7
+ if needing_publish.any?
8
+ ForemanTasks.async_task(::Actions::Katello::Repository::BulkMetadataGenerate, Katello::Repository.where(:id => needing_publish))
9
+ else
10
+ puts 'Skipped. No repository found which was created by the SCC plugin.'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ begin
2
+ require 'rubocop/rake_task'
3
+
4
+ namespace :foreman_scc_manager do
5
+ desc 'Set up authentication tokens for Katello 4.3.'
6
+ task :setup_authentication_tokens => ['environment'] do
7
+ unless ActiveRecord::Base.connection.column_exists?('katello_root_repositories', 'upstream_authentication_token')
8
+ puts 'Your Katello version needs to be at 4.3 and up to run this task.'
9
+ return
10
+ end
11
+ begin
12
+ scc_repositories = SccRepository.includes(:scc_katello_repositories).where.not(scc_katello_repositories: { id: nil })
13
+ scc_repositories.each do |scc_repo|
14
+ scc_repo.katello_root_repositories.each do |katello_repo|
15
+ katello_repo.update!(url: scc_repo.url, upstream_authentication_token: scc_repo.token)
16
+ end
17
+ end
18
+ puts 'Authentication tokens created successfully.'
19
+ rescue StandardError => e
20
+ puts 'There was an error while creating the authentication tokens.'
21
+ puts e.to_s
22
+ end
23
+ end
24
+ end
25
+ rescue LoadError
26
+ puts 'Rubocop not loaded.'
27
+ end