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