foreman_scc_manager 1.8.4 → 1.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -1
  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 -13
  8. data/app/lib/actions/scc_manager/sync_repositories.rb +9 -13
  9. data/app/models/scc_account.rb +63 -5
  10. data/app/models/scc_product.rb +20 -2
  11. data/app/models/scc_repository.rb +11 -9
  12. data/app/views/scc_accounts/_form.html.erb +6 -0
  13. data/app/views/scc_accounts/index.html.erb +4 -3
  14. data/app/views/scc_accounts/show.html.erb +13 -4
  15. data/db/migrate/20200520281300_fix_scc_permissions.rb +22 -0
  16. data/db/migrate/20201119084201_add_gpg_key_to_scc_account.rb +6 -0
  17. data/db/migrate/20210205082733_add_subscription_valid_to_scc_products_and_repos.rb +6 -0
  18. data/db/migrate/20210210104407_add_root_repository_id_to_scc_repository.rb +6 -0
  19. data/db/migrate/20210224095050_connect_katello_root_repository_to_scc_repository.rb +28 -0
  20. data/lib/foreman_scc_manager/engine.rb +40 -27
  21. data/lib/foreman_scc_manager/version.rb +1 -1
  22. data/lib/tasks/test.rake +1 -1
  23. data/locale/de/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  24. data/locale/de/foreman_scc_manager.edit.po +574 -0
  25. data/locale/de/foreman_scc_manager.po +40 -21
  26. data/locale/de/foreman_scc_manager.po.time_stamp +0 -0
  27. data/locale/en/foreman_scc_manager.edit.po +566 -0
  28. data/locale/en/foreman_scc_manager.po +21 -3
  29. data/locale/en/foreman_scc_manager.po.time_stamp +0 -0
  30. data/locale/foreman_scc_manager.pot +65 -38
  31. data/test/controllers/api/v2/scc_accounts_test.rb +9 -3
  32. data/test/controllers/scc_accounts_controller_test.rb +104 -0
  33. data/test/controllers/scc_accounts_controller_test2.rb +51 -0
  34. data/test/fixtures/models/scc_extendings.yml +6 -0
  35. data/test/fixtures/models/scc_products.yml +30 -2
  36. data/test/fixtures/models/scc_repositories.yml +29 -0
  37. data/test/models/scc_account_test.rb +70 -0
  38. data/test/models/scc_product_test.rb +69 -1
  39. data/test/support/fixtures_support.rb +3 -2
  40. data/test/test_controller_helper.rb +15 -0
  41. data/test/test_plugin_helper.rb +9 -0
  42. metadata +31 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ad2397229cd491566c2860224641db959be83c82254864cd06f5c37648cd60e
4
- data.tar.gz: f9a8e1e8f6ab872e1e0528beab2563421d498e5aaa32a38c21b00756d4cf84b6
3
+ metadata.gz: ab78f155bda869b47a6124ada7ebcdf9d067c6173c400dfd9c052c70227695f9
4
+ data.tar.gz: '009d1c1db82b1c52e4e0ab47ebdb1769dbb4ea0e71fb6a2a0b5b26fff5b0cb41'
5
5
  SHA512:
6
- metadata.gz: 962060eff9906bfb01a629a3700adaa460d5120d57e2823a19ce786f1aecafad65ddd903d2430a40a431f53a7ed341fd39103324382c9cf33fd07380b1cc49e4
7
- data.tar.gz: 2d3fd9536db98424c81f7b730727d5c7fd6034428e9cbd64eb238ed4b4b022267b4f6c2a39b1f6d6008f28e751d05da3341476d7f9366151f481d3ec95b8c91c
6
+ metadata.gz: da25af4b803899a68ef9b3fae9646901437051d5b69dd4b5c588cca5e0018ea0b797d32255ed4dc5ba6ccb5044a119c2d712bb3b35905aeb231182145f910878
7
+ data.tar.gz: 15b7aa5a3f7a05e3c517aa6aa41fcef1c9bee3fb00d5f49405166264d21a7857fc348940c06a0279d88330d29b7480e90d2f3e32025ee2bcba95c1acf84f1c7b
data/README.md CHANGED
@@ -13,6 +13,9 @@ for how to install Foreman plugins
13
13
 
14
14
  | Foreman Version | Katello Version | Plugin Version |
15
15
  | --------------- | --------------- | -------------- |
16
+ | 2.3 | 3.18 | ~> 1.8.9 |
17
+ | 2.1 | 3.16 | ~> 1.8.5 |
18
+ | 2.0 | 3.16 | ~> 1.8.4 |
16
19
  | 1.24 | 3.14 | ~> 1.8.0 |
17
20
  | 1.22 | 3.12 | ~> 1.7.0 |
18
21
  | 1.21 | 3.10 | ~> 1.6.0 |
@@ -23,13 +26,19 @@ for how to install Foreman plugins
23
26
  | 1.16 | 3.5 | <= 1.3.0 |
24
27
  | 1.15 | 3.4 | ~> 1.1.0 |
25
28
 
29
+ ## Documentation
30
+ The plugin documentation can be found at https://docs.orcharhino.com/sources/management_ui/the_content_menu/suse_subscriptions.html.
31
+
32
+ A Hammer CLI extension is available for this plugin:
33
+ https://github.com/ATIX-AG/hammer-cli-foreman-scc-manager
34
+
26
35
  ## Contributing
27
36
 
28
37
  Fork and send a Pull Request. Thanks!
29
38
 
30
39
  ## Copyright
31
40
 
32
- Copyright (c) 2017 ATIX AG - http://www.atix.de
41
+ Copyright (c) 2021 ATIX AG - http://www.atix.de
33
42
 
34
43
  This program is free software: you can redistribute it and/or modify
35
44
  it under the terms of the GNU General Public License as published by
@@ -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,22 +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 => e
24
- ::Foreman::Logging.logger('foreman_scc_manager').error "Error while syncronizing SCC-Products: #{e}"
25
- output[:error] = e.to_s
26
- output[:status] = 'FAILURE'
27
- 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
28
24
  end
29
25
 
30
26
  def finalize
31
- 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))
32
29
  end
33
30
 
34
31
  def rescue_strategy
@@ -12,22 +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 => e
23
- ::Foreman::Logging.logger('foreman_scc_manager').error "Error while syncronizing SCC-Repositories: #{e}"
24
- output[:error] = e.to_s
25
- output[:status] = 'FAILURE'
26
- 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
27
22
  end
28
23
 
29
24
  def finalize
30
- 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])
31
27
  end
32
28
 
33
29
  def rescue_strategy
@@ -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
@@ -173,21 +176,34 @@ class SccAccount < ApplicationRecord
173
176
 
174
177
  def update_scc_repositories(upstream_repositories)
175
178
  upstream_repo_ids = []
179
+ # initially invalidate all repositories and validate them during update
180
+ invalidated_repos = invalidate_subscription_status(scc_repositories)
176
181
  # import repositories
177
182
  upstream_repositories.each do |ur|
178
183
  cached_repository = scc_repositories.find_or_initialize_by(scc_id: ur['id'])
179
- cached_repository.name = ur['name']
180
184
  cached_repository.distro_target = ur['distro_target']
181
185
  cached_repository.description = ur['description']
182
186
  cached_repository.url, cached_repository.token = ur['url'].split('?')
183
187
  cached_repository.enabled = ur['enabled']
184
188
  cached_repository.autorefresh = ur['autorefresh']
185
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
186
193
  cached_repository.save!
187
194
  upstream_repo_ids << ur['id']
195
+ # set invalidated record to true, if exists
196
+ invalidated_repos = revalidate_subscription_status(invalidated_repos, ur[id])
188
197
  end
189
198
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Found #{upstream_repo_ids.length} repositories"
190
- # delete repositories beeing removed upstream
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
191
207
  to_delete = scc_repositories.where.not(scc_id: upstream_repo_ids)
192
208
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Deleting #{to_delete.count} old repositories"
193
209
  to_delete.destroy_all
@@ -195,10 +211,11 @@ class SccAccount < ApplicationRecord
195
211
 
196
212
  def update_scc_products(upstream_products)
197
213
  upstream_product_ids = []
214
+ # initially invalidate all products and validate them during update
215
+ invalidated_products = invalidate_subscription_status(scc_products)
198
216
  # import products
199
217
  upstream_products.each do |up|
200
218
  cached_product = scc_products.find_or_initialize_by(scc_id: up['id'])
201
- cached_product.name = up['name']
202
219
  cached_product.version = up['version']
203
220
  cached_product.arch = up['arch']
204
221
  cached_product.description = up['description']
@@ -206,12 +223,25 @@ class SccAccount < ApplicationRecord
206
223
  cached_product.product_type = up['product_type']
207
224
  cached_product.scc_repositories =
208
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
209
230
  cached_product.save!
210
231
  upstream_product_ids << up['id']
232
+ # set invalidated record to true, if exists
233
+ invalidated_products = revalidate_subscription_status(invalidated_products, up['id'])
211
234
  end
212
235
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Found #{upstream_product_ids.length} products"
213
- # delete products beeing removed upstream
214
- to_delete = scc_products.where.not(scc_id: upstream_product_ids)
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)
215
245
  ::Foreman::Logging.logger('foreman_scc_manager').debug "Deleting #{to_delete.count} old products"
216
246
  to_delete.destroy_all
217
247
  # rewire product to product relationships
@@ -224,4 +254,32 @@ class SccAccount < ApplicationRecord
224
254
  end
225
255
  end
226
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
281
+ end
282
+ end
283
+ items_to_invalidate
284
+ end
227
285
  end