foreman_scc_manager 1.8.2 → 1.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -0
  3. data/app/controllers/api/v2/scc_accounts_controller.rb +9 -7
  4. data/app/controllers/scc_accounts_controller.rb +30 -4
  5. data/app/lib/actions/scc_manager/subscribe_product.rb +26 -13
  6. data/app/lib/actions/scc_manager/sync.rb +5 -5
  7. data/app/lib/actions/scc_manager/sync_products.rb +10 -11
  8. data/app/lib/actions/scc_manager/sync_repositories.rb +9 -11
  9. data/app/lib/scc_manager.rb +2 -2
  10. data/app/models/scc_account.rb +70 -11
  11. data/app/models/scc_product.rb +20 -2
  12. data/app/models/scc_repository.rb +11 -9
  13. data/app/views/scc_accounts/_form.html.erb +6 -0
  14. data/app/views/scc_accounts/index.html.erb +4 -3
  15. data/app/views/scc_accounts/show.html.erb +13 -4
  16. data/db/migrate/20200520281300_fix_scc_permissions.rb +22 -0
  17. data/db/migrate/20201119084201_add_gpg_key_to_scc_account.rb +6 -0
  18. data/db/migrate/20210205082733_add_subscription_valid_to_scc_products_and_repos.rb +6 -0
  19. data/db/migrate/20210210104407_add_root_repository_id_to_scc_repository.rb +6 -0
  20. data/db/migrate/20210224095050_connect_katello_root_repository_to_scc_repository.rb +17 -0
  21. data/lib/foreman_scc_manager/engine.rb +36 -25
  22. data/lib/foreman_scc_manager/version.rb +1 -1
  23. data/lib/tasks/test.rake +1 -1
  24. data/locale/de/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  25. data/locale/de/foreman_scc_manager.edit.po +574 -0
  26. data/locale/de/foreman_scc_manager.po +40 -21
  27. data/locale/de/foreman_scc_manager.po.time_stamp +0 -0
  28. data/locale/en/foreman_scc_manager.edit.po +566 -0
  29. data/locale/en/foreman_scc_manager.po +21 -3
  30. data/locale/en/foreman_scc_manager.po.time_stamp +0 -0
  31. data/locale/foreman_scc_manager.pot +65 -38
  32. data/test/controllers/api/v2/scc_accounts_test.rb +9 -3
  33. data/test/controllers/scc_accounts_controller_test.rb +104 -0
  34. data/test/controllers/scc_accounts_controller_test2.rb +51 -0
  35. data/test/fixtures/models/scc_extendings.yml +6 -0
  36. data/test/fixtures/models/scc_products.yml +30 -2
  37. data/test/fixtures/models/scc_repositories.yml +29 -0
  38. data/test/models/scc_account_test.rb +70 -0
  39. data/test/models/scc_product_test.rb +69 -1
  40. data/test/support/fixtures_support.rb +3 -2
  41. data/test/test_controller_helper.rb +15 -0
  42. data/test/test_plugin_helper.rb +9 -0
  43. metadata +33 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb848728ea3961f30b44e6a02f43b84e73555ff703b0e0c3456e772e66132536
4
- data.tar.gz: e7ca63acbe79ac2def4f203a0f10c269230652a35efd53e08d7fec4c536f2a18
3
+ metadata.gz: 843165a6092fcb04bf69d13131ae44acb02478ce347084c160a8f61891770eba
4
+ data.tar.gz: 892025747b9572a4b31d25f4c96ab28ea9af0099ef3302fe8c7aec07935b588b
5
5
  SHA512:
6
- metadata.gz: 215eaa829b2d7e544d818e421850763f4d78bc2b3e62d38d397980c10d0d066bea28e6a9d1fcacfdcb93486a683c0350fdaa0ea80451aebb99f6a7761cda62cc
7
- data.tar.gz: a93f9a77bcc06abff4e22e78268363f5ae2a71be7d64debd8184ecfdeed412586f9e92bd8a5d7ef11d6308c7149950e7fd0114ef806c6d9717766cc838ed131c
6
+ metadata.gz: 0cb42eeb656e4a0cca5c93d7c83e0fed14fa73a9b0e7f2ad0ddcb07b007156405f0e91d968e4eb38aafd6376551f334340bda0a4ff9d632d13d4818766e7eb75
7
+ data.tar.gz: 700546d7abfdc1ff7fc12f452c69bb6ca8869c41b63ae4e37fef99541dc229bf2287de3435ecd8c09dee32cb79465b8c896af09c78b4d0d498209e35c8a347cf
data/README.md CHANGED
@@ -13,6 +13,8 @@ for how to install Foreman plugins
13
13
 
14
14
  | Foreman Version | Katello Version | Plugin Version |
15
15
  | --------------- | --------------- | -------------- |
16
+ | 2.1 | 3.16 | ~> 1.8.5 |
17
+ | 2.0 | 3.16 | ~> 1.8.4 |
16
18
  | 1.24 | 3.14 | ~> 1.8.0 |
17
19
  | 1.22 | 3.12 | ~> 1.7.0 |
18
20
  | 1.21 | 3.10 | ~> 1.6.0 |
@@ -23,6 +25,9 @@ for how to install Foreman plugins
23
25
  | 1.16 | 3.5 | <= 1.3.0 |
24
26
  | 1.15 | 3.4 | ~> 1.1.0 |
25
27
 
28
+ ## Documentation
29
+ The plugin documentation can be found at https://docs.orcharhino.com/sources/management_ui/the_content_menu/suse_subscriptions.html.
30
+
26
31
  ## Contributing
27
32
 
28
33
  Fork and send a Pull Request. Thanks!
@@ -18,7 +18,7 @@ module Api
18
18
  def index
19
19
  scope = resource_scope
20
20
  scope = scope.where(:organization => params[:organization_id]) if params[:organization_id].present?
21
- @scc_accounts = scope.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
21
+ @scc_accounts = scope.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page])
22
22
  end
23
23
 
24
24
  api :GET, '/scc_accounts/:id/', N_('Show scc_account')
@@ -34,7 +34,8 @@ module Api
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
36
  param :interval, ['never', 'daily', 'weekly', 'monthy'], :desc => N_('Interval for syncing scc_account')
37
- param :sync_date, String, :desc => N_('Last Sync time of scc_account')
37
+ param :sync_date, String, :desc => N_('Date and time relative to which the sync interval is run')
38
+ param :katello_gpg_key_id, :identifier, :required => false, :desc => N_('Associated GPG key of scc_account')
38
39
  end
39
40
  end
40
41
 
@@ -79,9 +80,9 @@ module Api
79
80
  end
80
81
  respond_to do |format|
81
82
  if @scc_account.test_connection
82
- format.json { render json: 'Success'.to_json, status: :ok }
83
+ format.json { render json: { 'success' => true }.to_json, status: :ok }
83
84
  else
84
- format.json { render json: 'Failed'.to_json, status: :not_found }
85
+ format.json { render json: { 'success' => false, 'error' => 'Test failed. Check your credentials.' }.to_json, status: :not_found }
85
86
  end
86
87
  end
87
88
  end
@@ -102,7 +103,7 @@ module Api
102
103
 
103
104
  api :PUT, '/scc_accounts/:id/bulk_subscribe/', N_('Bulk subscription of scc_products for scc_account')
104
105
  param :id, :identifier_dottable, :required => true
105
- param :scc_subscribe_product_ids, Array
106
+ param :scc_subscribe_product_ids, Array, :required => true
106
107
  def bulk_subscribe
107
108
  scc_products_to_subscribe = @scc_account.scc_products.where(:id => params[:scc_subscribe_product_ids])
108
109
  respond_to do |format|
@@ -112,7 +113,7 @@ module Api
112
113
  scc_products_to_subscribe)
113
114
  format.json { render json: subscribe_task.to_json, status: :ok }
114
115
  else
115
- format.json { render json: 'No Product selected'.to_json, status: :expectation_failed }
116
+ format.json { render json: { error: 'No Product selected' }, status: :expectation_failed }
116
117
  end
117
118
  end
118
119
  rescue ::Foreman::Exception => e
@@ -132,7 +133,8 @@ module Api
132
133
  :base_url,
133
134
  :interval,
134
135
  :sync_date,
135
- :organization_id
136
+ :organization_id,
137
+ :katello_gpg_key_id
136
138
  )
137
139
  end
138
140
 
@@ -1,12 +1,19 @@
1
1
  class SccAccountsController < ApplicationController
2
+ helper_method :scc_filtered_products
2
3
  before_action :find_organization
3
4
  before_action :find_resource, only: %i[show edit update destroy sync bulk_subscribe]
5
+ before_action :find_available_gpg_keys, only: %i[new edit]
4
6
  include Foreman::Controller::AutoCompleteSearch
5
7
 
6
8
  # GET /scc_accounts
7
9
  def index
8
- @scc_accounts = resource_base.search_for(params[:search], order: params[:order])
9
- .paginate(page: params[:page])
10
+ @scc_accounts = resource_base.where(organization: @organization)
11
+ .search_for(params[:search], order: params[:order])
12
+ .paginate(:page => params[:page], :per_page => params[:per_page])
13
+ # overwrite the product list with filtered products that do not include products with empty repositories
14
+ @scc_accounts.each do |scc_account|
15
+ scc_account.scc_products_with_repos_count = scc_account.scc_products.only_products_with_repos.count
16
+ end
10
17
  end
11
18
 
12
19
  # GET /scc_accounts/new
@@ -92,6 +99,11 @@ class SccAccountsController < ApplicationController
92
99
 
93
100
  private
94
101
 
102
+ def find_available_gpg_keys
103
+ @scc_account ? org = @scc_account.organization : org = @organization
104
+ @selectable_gpg_keys = ::Katello::GpgKey.where(organization: org).collect { |p| [p.name, p.id] }.unshift ['None', nil]
105
+ end
106
+
95
107
  def find_organization
96
108
  @organization = Organization.current
97
109
  redirect_to '/select_organization?toState=' + request.path unless @organization
@@ -108,7 +120,8 @@ class SccAccountsController < ApplicationController
108
120
  :base_url,
109
121
  :interval,
110
122
  :sync_date,
111
- :organization_id
123
+ :organization_id,
124
+ :katello_gpg_key_id
112
125
  )
113
126
  end
114
127
 
@@ -121,9 +134,22 @@ class SccAccountsController < ApplicationController
121
134
  when 'sync', 'test_connection'
122
135
  :sync
123
136
  when 'bulk_subscribe'
124
- :bulk_subscribe
137
+ :use
125
138
  else
126
139
  super
127
140
  end
128
141
  end
142
+
143
+ # Function filters a product list and removes all products without valid repositories
144
+ # The .order call is necessary to apply the ordering to repository that have already been loaded from the database.
145
+ # Input parameters:
146
+ # product_list: list of SccProduct
147
+ # product_type: return only base products if type is set (default), else all
148
+ def scc_filtered_products(product_list, product_type = 'base')
149
+ if product_type == 'base'
150
+ product_list.only_products_with_repos.where(product_type: 'base').order(:friendly_name)
151
+ else
152
+ product_list.only_products_with_repos.order(:friendly_name)
153
+ end
154
+ end
129
155
  end
@@ -8,27 +8,38 @@ module Actions
8
8
  .info("Initiating subscription for SccProduct '#{scc_product.friendly_name}'.")
9
9
  sequence do
10
10
  product_create_action = plan_action(CreateProduct,
11
- product_name: scc_product.uniq_name,
12
- product_description: scc_product.description,
13
- organization_id: scc_product.organization.id)
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)
15
+ katello_repos = {}
14
16
  scc_product.scc_repositories.each do |repo|
15
- uniq_name = scc_product.uniq_name + ' ' + repo.description
16
17
  arch = scc_product.arch || 'noarch'
17
- plan_action(CreateRepository,
18
- :product_id => product_create_action.output[:product_id],
19
- :uniq_name => uniq_name,
20
- :url => repo.full_url,
21
- :arch => arch)
18
+ repo_create_action = plan_action(CreateRepository,
19
+ :product_id => product_create_action.output[:product_id],
20
+ :uniq_name => repo.uniq_name(scc_product),
21
+ :pretty_repo_name => repo.pretty_name,
22
+ :url => repo.full_url,
23
+ :arch => arch)
24
+ katello_repos[repo.id] = repo_create_action.output[:katello_root_repository_id]
22
25
  end
26
+ # connect action to resource (=> make parameters accessable in input)
23
27
  action_subject(scc_product, product_id: product_create_action.output[:product_id])
28
+ input.update(katello_repos: katello_repos)
24
29
  plan_self
25
30
  end
26
31
  end
27
32
 
28
33
  def finalize
34
+ # connect Scc products and Katello products
29
35
  scc_product = SccProduct.find(input[:scc_product][:id])
30
36
  product = ::Katello::Product.find(input[:product_id])
31
37
  scc_product.update!(product: product)
38
+ # extract Katello repo ids from input hash and store to database
39
+ 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)
42
+ end
32
43
  end
33
44
 
34
45
  def humanized_name
@@ -43,6 +54,7 @@ module Actions
43
54
  def create_sub_plans
44
55
  product = ::Katello::Product.new
45
56
  product.name = input[:product_name]
57
+ product.gpg_key = ::Katello::GpgKey.find_by(id: input[:gpg_key], organization: input[:organization_id])
46
58
  product.description = input[:product_description]
47
59
  trigger(::Actions::Katello::Product::Create,
48
60
  product,
@@ -58,12 +70,11 @@ module Actions
58
70
 
59
71
  def create_sub_plans
60
72
  product = ::Katello::Product.find(input[:product_id])
61
- uniq_name = input[:uniq_name]
62
- label = ::Katello::Util::Model.labelize(uniq_name)
73
+ label = ::Katello::Util::Model.labelize(input[:uniq_name])
63
74
  unprotected = true
64
75
  gpg_key = product.gpg_key
65
76
  repo_param = { :label => label,
66
- :name => uniq_name,
77
+ :name => input[:pretty_repo_name],
67
78
  :url => input[:url],
68
79
  :content_type => 'yum',
69
80
  :unprotected => unprotected,
@@ -73,7 +84,9 @@ module Actions
73
84
  repository = product.add_repo(repo_param)
74
85
  repository.mirror_on_sync = true
75
86
  repository.verify_ssl_on_sync = true
76
- trigger(::Actions::Katello::Repository::CreateRoot, repository)
87
+ trigger(::Actions::Katello::Repository::CreateRoot, repository).tap do
88
+ output[:katello_root_repository_id] = repository.id
89
+ end
77
90
  end
78
91
  end
79
92
  end
@@ -1,21 +1,21 @@
1
1
  module Actions
2
2
  module SccManager
3
+ # for dynflow documentation see here: https://dynflow.github.io/documentation/
3
4
  class Sync < Actions::EntryAction
4
5
  def plan(scc_account)
5
6
  ::Foreman::Logging.logger('foreman_scc_manager')
6
7
  .info("Initiating 'sync' for SccAccount '#{scc_account.name}'.")
7
8
  action_subject(scc_account)
8
9
  sequence do
9
- sync_repo_action = plan_action(::Actions::SccManager::SyncRepositories, scc_account)
10
- sync_prod_action = plan_action(::Actions::SccManager::SyncProducts, scc_account)
11
- plan_self(repo_status: sync_repo_action.output[:status], prod_status: sync_prod_action.output[:status])
10
+ plan_action(::Actions::SccManager::SyncRepositories, scc_account)
11
+ plan_action(::Actions::SccManager::SyncProducts, scc_account)
12
+ plan_self
12
13
  end
13
14
  end
14
15
 
15
16
  def finalize
17
+ # this is only executed if run actions of SyncRepositories and SyncProducts were successful
16
18
  scc_account = SccAccount.find(input[:scc_account][:id])
17
- raise 'Updating failed' unless input[:repo_status] == 'SUCCESS' && input[:prod_status] == 'SUCCESS'
18
-
19
19
  scc_account.update! synced: Time.current
20
20
  end
21
21
 
@@ -13,20 +13,19 @@ module Actions
13
13
  end
14
14
 
15
15
  def run
16
- output[:status] = 'SUCCESS'
17
- begin
18
- products = ::SccManager.get_scc_data(input.fetch(:base_url),
19
- '/connect/organizations/products',
20
- input.fetch(:login),
21
- decrypt_field(input.fetch(:password)))
22
- output[:data] = ::SccManager.sanitize_products(products).values
23
- rescue StandardError
24
- output[:status] = 'FAILURE'
25
- end
16
+ products = ::SccManager.get_scc_data(input.fetch(:base_url),
17
+ '/connect/organizations/products',
18
+ input.fetch(:login),
19
+ decrypt_field(input.fetch(:password)))
20
+ output[:data] = ::SccManager.sanitize_products(products).values
21
+ rescue StandardError => e
22
+ ::Foreman::Logging.logger('foreman_scc_manager').error "Error while syncronizing SCC-Products: #{e}"
23
+ error! e.to_s
26
24
  end
27
25
 
28
26
  def finalize
29
- SccAccount.find(input.fetch(:id)).update_scc_products(output.fetch(:data)) if output[:status] == 'SUCCESS'
27
+ # this is only executed if 'run' succeeds
28
+ SccAccount.find(input.fetch(:id)).update_scc_products(output.fetch(:data))
30
29
  end
31
30
 
32
31
  def rescue_strategy
@@ -12,20 +12,18 @@ module Actions
12
12
  end
13
13
 
14
14
  def run
15
- output[:status] = 'IN PROGRESS'
16
- begin
17
- output[:data] = ::SccManager.get_scc_data(input[:base_url],
18
- '/connect/organizations/repositories',
19
- input[:login],
20
- decrypt_field(input[:password]))
21
- output[:status] = 'SUCCESS'
22
- rescue StandardError
23
- output[:status] = 'FAILURE'
24
- end
15
+ output[:data] = ::SccManager.get_scc_data(input[:base_url],
16
+ '/connect/organizations/repositories',
17
+ input[:login],
18
+ decrypt_field(input[:password]))
19
+ rescue StandardError => e
20
+ ::Foreman::Logging.logger('foreman_scc_manager').error "Error while syncronizing SCC-Repositories: #{e}"
21
+ error! e.to_s
25
22
  end
26
23
 
27
24
  def finalize
28
- SccAccount.find(input[:scc_account][:id]).update_scc_repositories(output[:data]) if output[:status] == 'SUCCESS'
25
+ # this is only executed if 'run' succeeds
26
+ SccAccount.find(input[:scc_account][:id]).update_scc_repositories(output[:data])
29
27
  end
30
28
 
31
29
  def rescue_strategy
@@ -8,8 +8,8 @@ module SccManager
8
8
  uri.scheme = URI.parse(proxy_config[:host]).scheme
9
9
  uri.host = URI.parse(proxy_config[:host]).host
10
10
  uri.port = proxy_config[:port].try(:to_s)
11
- uri.user = proxy_config[:user].try(:to_s)
12
- uri.password = proxy_config[:password].try(:to_s)
11
+ uri.user = proxy_config[:user]
12
+ uri.password = proxy_config[:password] if uri.user.present?
13
13
 
14
14
  RestClient.proxy = uri.to_s
15
15
  end
@@ -2,6 +2,9 @@ class SccAccount < ApplicationRecord
2
2
  include Authorizable
3
3
  include Encryptable
4
4
  include ForemanTasks::Concerns::ActionSubject
5
+
6
+ attr_accessor :scc_products_with_repos_count
7
+
5
8
  encrypts :password
6
9
 
7
10
  NEVER = 'never'.freeze
@@ -166,38 +169,53 @@ class SccAccount < ApplicationRecord
166
169
  def test_connection
167
170
  get_scc_data('/connect/organizations/subscriptions')
168
171
  true
169
- rescue StandardError
172
+ rescue StandardError => e
173
+ ::Foreman::Logging.logger('foreman_scc_manager').warn "Error occurred while testing SCC-Connection to Account #{self}: #{e}"
170
174
  false
171
175
  end
172
176
 
173
177
  def update_scc_repositories(upstream_repositories)
174
178
  upstream_repo_ids = []
179
+ # initially invalidate all repositories and validate them during update
180
+ invalidated_repos = invalidate_subscription_status(scc_repositories)
175
181
  # import repositories
176
182
  upstream_repositories.each do |ur|
177
183
  cached_repository = scc_repositories.find_or_initialize_by(scc_id: ur['id'])
178
- cached_repository.name = ur['name']
179
184
  cached_repository.distro_target = ur['distro_target']
180
185
  cached_repository.description = ur['description']
181
186
  cached_repository.url, cached_repository.token = ur['url'].split('?')
182
187
  cached_repository.enabled = ur['enabled']
183
188
  cached_repository.autorefresh = ur['autorefresh']
184
189
  cached_repository.installer_updates = ur['installer_updates']
190
+ # should be called after all attributes are set in case of dependencies (currently: description)
191
+ cached_repository.name = cached_repository.pretty_name
192
+ cached_repository.subscription_valid = true
185
193
  cached_repository.save!
186
194
  upstream_repo_ids << ur['id']
195
+ # set invalidated record to true, if exists
196
+ invalidated_repos = revalidate_subscription_status(invalidated_repos, ur[id])
187
197
  end
188
- logger.debug "Found #{upstream_repo_ids.length} repositories"
189
- # delete repositories beeing removed upstream
198
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Found #{upstream_repo_ids.length} repositories"
199
+
200
+ # all scc repos that are kept but not available upstream need to be marked invalid
201
+ # subscription_valid can be set to nil
202
+ to_invalidate = invalidated_repos.select { |ir| ir.katello_root_repository_id.present? && !ir.subscription_valid }
203
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Invalidating #{to_invalidate.count} expired repositories"
204
+ invalidate_subscription_status(to_invalidate, true)
205
+
206
+ # delete repositories being removed upstream and that are not subscribed to
190
207
  to_delete = scc_repositories.where.not(scc_id: upstream_repo_ids)
191
- logger.debug "Deleting #{to_delete.count} old repositories"
208
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Deleting #{to_delete.count} old repositories"
192
209
  to_delete.destroy_all
193
210
  end
194
211
 
195
212
  def update_scc_products(upstream_products)
196
213
  upstream_product_ids = []
214
+ # initially invalidate all products and validate them during update
215
+ invalidated_products = invalidate_subscription_status(scc_products)
197
216
  # import products
198
217
  upstream_products.each do |up|
199
218
  cached_product = scc_products.find_or_initialize_by(scc_id: up['id'])
200
- cached_product.name = up['name']
201
219
  cached_product.version = up['version']
202
220
  cached_product.arch = up['arch']
203
221
  cached_product.description = up['description']
@@ -205,13 +223,26 @@ class SccAccount < ApplicationRecord
205
223
  cached_product.product_type = up['product_type']
206
224
  cached_product.scc_repositories =
207
225
  scc_repositories.where(scc_id: up['repositories'].map { |repo| repo['id'] })
226
+ # name should be set after friendly_name because it depends on friendly_name
227
+ cached_product.name = cached_product.pretty_name
228
+ cached_product.description = cached_product.pretty_description
229
+ cached_product.subscription_valid = true
208
230
  cached_product.save!
209
231
  upstream_product_ids << up['id']
232
+ # set invalidated record to true, if exists
233
+ invalidated_products = revalidate_subscription_status(invalidated_products, up['id'])
210
234
  end
211
- logger.debug "Found #{upstream_product_ids.length} products"
212
- # delete products beeing removed upstream
213
- to_delete = scc_products.where.not(scc_id: upstream_product_ids)
214
- logger.debug "Deleting #{to_delete.count} old products"
235
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Found #{upstream_product_ids.length} products"
236
+
237
+ # all scc products that are kept but not available upstream need to be marked invalid
238
+ # subscription_valid can be set to nil
239
+ to_invalidate = invalidated_products.select { |ip| ip.product_id.present? && !ip.subscription_valid }
240
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Invalidating #{to_invalidate.count} expired products"
241
+ invalidate_subscription_status(to_invalidate, true)
242
+
243
+ # delete products being removed upstream and that are not subscribed to
244
+ to_delete = scc_products.where.not(scc_id: upstream_product_ids).where(product_id: nil)
245
+ ::Foreman::Logging.logger('foreman_scc_manager').debug "Deleting #{to_delete.count} old products"
215
246
  to_delete.destroy_all
216
247
  # rewire product to product relationships
217
248
  upstream_products.each do |up|
@@ -219,8 +250,36 @@ class SccAccount < ApplicationRecord
219
250
  begin
220
251
  scc_products.find_by!(scc_id: up['id']).update!(scc_extensions: extensions)
221
252
  rescue ActiveRecord::RecordNotFound
222
- logger.info "Failed to find parent scc_product '#{up['name']}'."
253
+ ::Foreman::Logging.logger('foreman_scc_manager').info "Failed to find parent scc_product '#{up['name']}'."
254
+ end
255
+ end
256
+ end
257
+
258
+ # validate the subscription status of a product/repo
259
+ # no saving to database
260
+ # params: elements: scc repos or products, Array or ActiveRecord_(*)
261
+ # scc_id: scc_id of the element that should be revalidated
262
+ # return: elements where for the element with scc_id subscription_valid is true
263
+ def revalidate_subscription_status(elements, scc_id)
264
+ return nil if elements.nil?
265
+
266
+ revalidate = elements.find { |e| e.scc_id == scc_id }
267
+ revalidate.subscription_valid = true unless revalidate.nil?
268
+ # return modified list
269
+ elements
270
+ end
271
+
272
+ # set all products/repos invalid
273
+ # params: items_to_invalidate: ActiveRecord_(*)
274
+ # save_record: store in database or not (default)
275
+ # return: ActiveRecord elements with invalidated subscription status
276
+ def invalidate_subscription_status(items_to_invalidate, save_record = false)
277
+ if items_to_invalidate.present?
278
+ items_to_invalidate.each do |inv|
279
+ inv.subscription_valid = false
280
+ inv.save! if save_record
223
281
  end
224
282
  end
283
+ items_to_invalidate
225
284
  end
226
285
  end