foreman_scc_manager 1.8.19 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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