foreman_scc_manager 1.8.20 → 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.
- checksums.yaml +4 -4
- data/README.md +14 -10
- data/app/controllers/api/v2/scc_accounts_controller.rb +47 -4
- data/app/controllers/api/v2/scc_products_controller.rb +1 -1
- data/app/controllers/scc_accounts_controller.rb +47 -0
- data/app/lib/actions/scc_manager/subscribe_product.rb +51 -15
- data/app/models/scc_account.rb +1 -1
- data/app/views/scc_accounts/show.html.erb +7 -51
- data/config/routes.rb +1 -0
- data/db/migrate/20220429102717_populate_scc_katello_repositories.rb +5 -0
- data/db/migrate/20220531120722_add_product_category_to_scc_products.rb +5 -0
- data/lib/foreman_scc_manager/engine.rb +1 -1
- data/lib/foreman_scc_manager/version.rb +1 -1
- data/lib/tasks/republish_repositories.rake +13 -0
- data/package.json +54 -0
- data/test/controllers/scc_accounts_controller_test.rb +6 -0
- data/webpack/components/SCCProductPage/EmptySccProducts.js +46 -0
- data/webpack/components/SCCProductPage/SCCProductPage.js +96 -0
- data/webpack/components/SCCProductPage/SCCProductPageActions.js +27 -0
- data/webpack/components/SCCProductPage/SCCProductPageConstants.js +5 -0
- data/webpack/components/SCCProductPage/SCCProductPageReducer.js +34 -0
- data/webpack/components/SCCProductPage/SCCProductPageSelectors.js +7 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCGenericPicker/index.js +80 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/index.js +174 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/styles.scss +3 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/index.js +303 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/index.js +235 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/styles.scss +10 -0
- data/webpack/components/SCCProductPage/components/SCCProductPickerModal/index.js +81 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/index.js +113 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/styles.scss +14 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/index.js +236 -0
- data/webpack/components/SCCProductPage/components/common/SCCGenericExpander/index.js +58 -0
- data/webpack/components/SCCProductPage/components/common/SCCProductTreeExpander/index.js +21 -0
- data/webpack/components/SCCProductPage/components/common/SCCSubscribedProductsExpander/index.js +21 -0
- data/webpack/components/SCCProductPage/index.js +18 -0
- data/webpack/components/SCCProductPage/sccProductPage.scss +8 -0
- data/webpack/index.js +11 -0
- data/webpack/reducer.js +7 -0
- metadata +40 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73d2510abbdbe51fef59bba613502fb02ef1fe14eeb441f0bee716b17e2eba8d
|
4
|
+
data.tar.gz: c61b9f852d58033c290204508d0d4653e5379c6c8b73e6d5b58d026ea485b0f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
28
|
-
|
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)
|
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
|
@@ -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
|
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
|
-
|
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,22 +1,54 @@
|
|
1
1
|
module Actions
|
2
2
|
module SccManager
|
3
3
|
class SubscribeProduct < Actions::EntryAction
|
4
|
-
|
5
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
54
|
:url => repo.url,
|
@@ -24,18 +56,22 @@ module Actions
|
|
24
56
|
:arch => arch)
|
25
57
|
katello_repos[repo.id] = repo_create_action.output[:katello_root_repository_id]
|
26
58
|
end
|
59
|
+
|
27
60
|
# connect action to resource (=> make parameters accessable in input)
|
28
|
-
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?
|
29
62
|
input.update(katello_repos: katello_repos)
|
30
63
|
plan_self
|
31
64
|
end
|
32
65
|
end
|
66
|
+
# rubocop:enable Metrics/MethodLength
|
33
67
|
|
34
68
|
def finalize
|
35
69
|
# connect Scc products and Katello products
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
39
75
|
# extract Katello repo ids from input hash and store to database
|
40
76
|
input[:katello_repos].each do |scc_repo_id, katello_root_repository_id|
|
41
77
|
SccKatelloRepository.find_or_create_by(scc_repository_id: scc_repo_id, katello_root_repository_id: katello_root_repository_id)
|
data/app/models/scc_account.rb
CHANGED
@@ -219,8 +219,8 @@ class SccAccount < ApplicationRecord
|
|
219
219
|
cached_product.product_type = up['product_type']
|
220
220
|
cached_product.scc_repositories =
|
221
221
|
scc_repositories.where(scc_id: up['repositories'].map { |repo| repo['id'] })
|
222
|
-
# name should be set after friendly_name because it depends on friendly_name
|
223
222
|
cached_product.name = cached_product.pretty_name
|
223
|
+
cached_product.product_category = up['name']
|
224
224
|
cached_product.description = cached_product.pretty_description
|
225
225
|
cached_product.subscription_valid = true
|
226
226
|
cached_product.save!
|
@@ -1,52 +1,8 @@
|
|
1
1
|
<% title (_("Product Selection for Account %s") % @scc_account) %>
|
2
|
-
|
3
|
-
|
4
|
-
<%=
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
@@ -3,6 +3,11 @@ class PopulateSccKatelloRepositories < ActiveRecord::Migration[6.0]
|
|
3
3
|
belongs_to :product, class_name: 'Katello::Product'
|
4
4
|
has_and_belongs_to_many :scc_repositories
|
5
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
|
6
11
|
|
7
12
|
def up
|
8
13
|
SccProduct.where.not(product_id: nil).each do |scc_p|
|
@@ -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,
|
@@ -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
|
data/package.json
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
{
|
2
|
+
"name": "foreman_scc_manager",
|
3
|
+
"version": "1.8.16",
|
4
|
+
"description": "Foreman plugin to sync SUSE Customer Center products and repositories into Katello ",
|
5
|
+
"main": "index.js",
|
6
|
+
"directories": {
|
7
|
+
"lib": "lib",
|
8
|
+
"test": "test"
|
9
|
+
},
|
10
|
+
"scripts": {
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
12
|
+
"create-react-component": "yo react-domain"
|
13
|
+
},
|
14
|
+
"repository": {
|
15
|
+
"type": "git",
|
16
|
+
"url": "git+ssh://git@github.com/ATIX-AG/foreman_scc_manager.git"
|
17
|
+
},
|
18
|
+
"peerDependencies": {
|
19
|
+
"@theforeman/vendor": "^4.14.0"
|
20
|
+
},
|
21
|
+
"devDependencies": {
|
22
|
+
"@babel/core": "^7.7.0",
|
23
|
+
"babel-eslint": "^10.0.0",
|
24
|
+
"babel-loader": "^8.0.0",
|
25
|
+
"@theforeman/builder": "^4.14.0",
|
26
|
+
"@theforeman/eslint-plugin-foreman": "^8.16.0",
|
27
|
+
"@theforeman/find-foreman": "^4.14.0",
|
28
|
+
"@theforeman/stories": "^4.14.0",
|
29
|
+
"@theforeman/test": "^4.14.0",
|
30
|
+
"@theforeman/vendor-dev": "^4.14.0",
|
31
|
+
"eslint": "^6.7.2",
|
32
|
+
"eslint-plugin-spellcheck": "0.0.17",
|
33
|
+
"eslint-plugin-react": "^7.27.1",
|
34
|
+
"eslint-plugin-react-hooks": "^4.3.0",
|
35
|
+
"prettier": "^2.5.1",
|
36
|
+
"react-redux-test-utils": "^0.2.0"
|
37
|
+
},
|
38
|
+
"keywords": [
|
39
|
+
"SUSE",
|
40
|
+
"Katello",
|
41
|
+
"products",
|
42
|
+
"repositories"
|
43
|
+
],
|
44
|
+
"author": "ATIX AG",
|
45
|
+
"license": "GPL-3.0",
|
46
|
+
"bugs": {
|
47
|
+
"url": "https://github.com/ATIX-AG/foreman_scc_manager/issues"
|
48
|
+
},
|
49
|
+
"homepage": "https://github.com/ATIX-AG/foreman_scc_manager#readme",
|
50
|
+
"dependencies": {
|
51
|
+
"react-native-elements": "^3.4.2",
|
52
|
+
"react-native-vector-icons": "^9.0.0"
|
53
|
+
}
|
54
|
+
}
|
@@ -2,6 +2,12 @@ require 'test_plugin_helper'
|
|
2
2
|
|
3
3
|
class SccAccountsControllerTest < ActionController::TestCase
|
4
4
|
def setup
|
5
|
+
# rubocop: disable Lint/SuppressedException
|
6
|
+
begin
|
7
|
+
::Katello::ContentCredential
|
8
|
+
rescue NameError
|
9
|
+
end
|
10
|
+
# rubocop: enable Lint/SuppressedException
|
5
11
|
@scc_account = scc_accounts(:one)
|
6
12
|
end
|
7
13
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useDispatch } from 'react-redux';
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import { Button } from '@patternfly/react-core';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import EmptyState from 'foremanReact/components/common/EmptyState';
|
7
|
+
import { syncSccAccountAction } from './SCCProductPageActions';
|
8
|
+
|
9
|
+
export const EmptySccProducts = ({ canCreate, sccAccountId }) => {
|
10
|
+
const dispatch = useDispatch();
|
11
|
+
const onSyncStart = (evt) => {
|
12
|
+
dispatch(syncSccAccountAction(sccAccountId));
|
13
|
+
};
|
14
|
+
|
15
|
+
const content = __(
|
16
|
+
`Please synchronize your SUSE account before you can subscribe to SUSE products.`
|
17
|
+
);
|
18
|
+
return (
|
19
|
+
<>
|
20
|
+
<EmptyState
|
21
|
+
icon="th"
|
22
|
+
iconType="fa"
|
23
|
+
header={__('SUSE Customer Center')}
|
24
|
+
description={<div dangerouslySetInnerHTML={{ __html: content }} />}
|
25
|
+
documentation={{
|
26
|
+
url: 'https://docs.orcharhino.com/or/docs/sources/usage_guides/managing_sles_systems_guide.html#mssg_adding_scc_accounts',
|
27
|
+
}}
|
28
|
+
/>
|
29
|
+
<Button onClick={onSyncStart} className="btn btn-primary">
|
30
|
+
{__('Synchronize SUSE Account')}
|
31
|
+
</Button>
|
32
|
+
</>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
|
36
|
+
EmptySccProducts.propTypes = {
|
37
|
+
canCreate: PropTypes.bool,
|
38
|
+
sccAccountId: PropTypes.number,
|
39
|
+
};
|
40
|
+
|
41
|
+
EmptySccProducts.defaultProps = {
|
42
|
+
canCreate: false,
|
43
|
+
sccAccountId: undefined,
|
44
|
+
};
|
45
|
+
|
46
|
+
export default EmptySccProducts;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
2
|
+
import React, { useState } from 'react';
|
3
|
+
import { Stack, StackItem } from '@patternfly/react-core';
|
4
|
+
import { useDispatch } from 'react-redux';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
|
7
|
+
import SCCProductView from './components/SCCProductView';
|
8
|
+
import EmptySccProducts from './EmptySccProducts';
|
9
|
+
import SCCProductPicker from './components/SCCProductPicker';
|
10
|
+
import SCCProductPickerModal from './components/SCCProductPickerModal';
|
11
|
+
import { SCCPRODUCTPAGE_SUMMARY_MODAL_ID } from './SCCProductPageConstants';
|
12
|
+
import './sccProductPage.scss';
|
13
|
+
|
14
|
+
const SCCProductPage = ({
|
15
|
+
canCreate,
|
16
|
+
sccAccountId,
|
17
|
+
sccProductsInit,
|
18
|
+
...props
|
19
|
+
}) => {
|
20
|
+
const dispatch = useDispatch();
|
21
|
+
const [productToEdit, setProductToEdit] = useState(0);
|
22
|
+
const [reposToSubscribe, setReposToSubscribe] = useState([]);
|
23
|
+
const [subscriptionTaskId, setSubscriptionTaskId] = useState();
|
24
|
+
|
25
|
+
const editProductTree = (productId) => {
|
26
|
+
setProductToEdit(productId);
|
27
|
+
};
|
28
|
+
|
29
|
+
const { setModalOpen, setModalClosed } = useForemanModal({
|
30
|
+
id: SCCPRODUCTPAGE_SUMMARY_MODAL_ID,
|
31
|
+
});
|
32
|
+
|
33
|
+
const handleSubscribeCallback = (
|
34
|
+
subscriptionTaskIdFromAction,
|
35
|
+
reposToSubscribeFromAction
|
36
|
+
) => {
|
37
|
+
setSubscriptionTaskId(subscriptionTaskIdFromAction);
|
38
|
+
const newReposToSubscribe = [];
|
39
|
+
Object.keys(reposToSubscribeFromAction).forEach((k) => {
|
40
|
+
const repo = {
|
41
|
+
productName: reposToSubscribeFromAction[k].productName,
|
42
|
+
repoNames: reposToSubscribeFromAction[k].repoNames,
|
43
|
+
};
|
44
|
+
newReposToSubscribe.push(repo);
|
45
|
+
});
|
46
|
+
setReposToSubscribe(newReposToSubscribe);
|
47
|
+
dispatch(setModalOpen({ id: SCCPRODUCTPAGE_SUMMARY_MODAL_ID }));
|
48
|
+
};
|
49
|
+
|
50
|
+
return sccProductsInit.length > 0 ? (
|
51
|
+
<Stack>
|
52
|
+
<StackItem>
|
53
|
+
<SCCProductPickerModal
|
54
|
+
id={SCCPRODUCTPAGE_SUMMARY_MODAL_ID}
|
55
|
+
title={__('The subscription task has been started successfully')}
|
56
|
+
taskId={subscriptionTaskId}
|
57
|
+
reposToSubscribe={reposToSubscribe}
|
58
|
+
/>
|
59
|
+
</StackItem>
|
60
|
+
<StackItem>
|
61
|
+
<SCCProductView
|
62
|
+
sccProducts={sccProductsInit.filter(
|
63
|
+
(prod) => prod.product_id !== null
|
64
|
+
)}
|
65
|
+
subscriptionTaskId={subscriptionTaskId}
|
66
|
+
editProductTreeGlobal={editProductTree}
|
67
|
+
/>
|
68
|
+
</StackItem>
|
69
|
+
<br />
|
70
|
+
<StackItem />
|
71
|
+
<StackItem>
|
72
|
+
<SCCProductPicker
|
73
|
+
sccProducts={sccProductsInit}
|
74
|
+
sccAccountId={sccAccountId}
|
75
|
+
editProductId={productToEdit}
|
76
|
+
handleSubscribeCallback={handleSubscribeCallback}
|
77
|
+
/>
|
78
|
+
</StackItem>
|
79
|
+
</Stack>
|
80
|
+
) : (
|
81
|
+
<EmptySccProducts sccAccountId={sccAccountId} canCreate={canCreate} />
|
82
|
+
);
|
83
|
+
};
|
84
|
+
|
85
|
+
SCCProductPage.propTypes = {
|
86
|
+
canCreate: PropTypes.bool,
|
87
|
+
sccAccountId: PropTypes.number.isRequired,
|
88
|
+
sccProductsInit: PropTypes.array,
|
89
|
+
};
|
90
|
+
|
91
|
+
SCCProductPage.defaultProps = {
|
92
|
+
canCreate: false,
|
93
|
+
sccProductsInit: [],
|
94
|
+
};
|
95
|
+
|
96
|
+
export default SCCProductPage;
|