foreman_openbolt 0.0.1

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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +46 -0
  4. data/Rakefile +106 -0
  5. data/app/controllers/foreman_openbolt/task_controller.rb +298 -0
  6. data/app/lib/actions/foreman_openbolt/cleanup_proxy_artifacts.rb +40 -0
  7. data/app/lib/actions/foreman_openbolt/poll_task_status.rb +151 -0
  8. data/app/models/foreman_openbolt/task_job.rb +110 -0
  9. data/app/views/foreman_openbolt/react_page.html.erb +1 -0
  10. data/config/routes.rb +24 -0
  11. data/db/migrate/20250819000000_create_openbolt_task_jobs.rb +25 -0
  12. data/db/migrate/20250925000000_add_command_to_openbolt_task_jobs.rb +7 -0
  13. data/db/migrate/20251001000000_add_task_description_to_task_jobs.rb +7 -0
  14. data/db/seeds.d/001_add_openbolt_feature.rb +4 -0
  15. data/lib/foreman_openbolt/engine.rb +169 -0
  16. data/lib/foreman_openbolt/version.rb +5 -0
  17. data/lib/foreman_openbolt.rb +7 -0
  18. data/lib/proxy_api/openbolt.rb +53 -0
  19. data/lib/tasks/foreman_openbolt_tasks.rake +48 -0
  20. data/locale/Makefile +73 -0
  21. data/locale/en/foreman_openbolt.po +19 -0
  22. data/locale/foreman_openbolt.pot +19 -0
  23. data/locale/gemspec.rb +7 -0
  24. data/package.json +41 -0
  25. data/test/factories/foreman_openbolt_factories.rb +7 -0
  26. data/test/test_plugin_helper.rb +8 -0
  27. data/test/unit/foreman_openbolt_test.rb +13 -0
  28. data/webpack/global_index.js +4 -0
  29. data/webpack/global_test_setup.js +11 -0
  30. data/webpack/index.js +19 -0
  31. data/webpack/src/Components/LaunchTask/EmptyContent.js +24 -0
  32. data/webpack/src/Components/LaunchTask/FieldTable.js +147 -0
  33. data/webpack/src/Components/LaunchTask/HostSelector/HostSearch.js +29 -0
  34. data/webpack/src/Components/LaunchTask/HostSelector/SearchSelect.js +208 -0
  35. data/webpack/src/Components/LaunchTask/HostSelector/SelectedChips.js +113 -0
  36. data/webpack/src/Components/LaunchTask/HostSelector/hostgroups.gql +9 -0
  37. data/webpack/src/Components/LaunchTask/HostSelector/hosts.gql +10 -0
  38. data/webpack/src/Components/LaunchTask/HostSelector/index.js +261 -0
  39. data/webpack/src/Components/LaunchTask/OpenBoltOptionsSection.js +116 -0
  40. data/webpack/src/Components/LaunchTask/ParameterField.js +145 -0
  41. data/webpack/src/Components/LaunchTask/ParametersSection.js +66 -0
  42. data/webpack/src/Components/LaunchTask/SmartProxySelect.js +51 -0
  43. data/webpack/src/Components/LaunchTask/TaskSelect.js +84 -0
  44. data/webpack/src/Components/LaunchTask/hooks/useOpenBoltOptions.js +63 -0
  45. data/webpack/src/Components/LaunchTask/hooks/useSmartProxies.js +48 -0
  46. data/webpack/src/Components/LaunchTask/hooks/useTasksData.js +64 -0
  47. data/webpack/src/Components/LaunchTask/index.js +333 -0
  48. data/webpack/src/Components/TaskExecution/ExecutionDetails.js +188 -0
  49. data/webpack/src/Components/TaskExecution/ExecutionDisplay.js +99 -0
  50. data/webpack/src/Components/TaskExecution/LoadingIndicator.js +51 -0
  51. data/webpack/src/Components/TaskExecution/ResultDisplay.js +174 -0
  52. data/webpack/src/Components/TaskExecution/TaskDetails.js +99 -0
  53. data/webpack/src/Components/TaskExecution/hooks/useJobPolling.js +142 -0
  54. data/webpack/src/Components/TaskExecution/index.js +130 -0
  55. data/webpack/src/Components/TaskHistory/TaskPopover.js +95 -0
  56. data/webpack/src/Components/TaskHistory/index.js +199 -0
  57. data/webpack/src/Components/common/HostsPopover.js +49 -0
  58. data/webpack/src/Components/common/constants.js +44 -0
  59. data/webpack/src/Components/common/helpers.js +19 -0
  60. data/webpack/src/Pages/LaunchTaskPage.js +12 -0
  61. data/webpack/src/Pages/TaskExecutionPage.js +12 -0
  62. data/webpack/src/Pages/TaskHistoryPage.js +12 -0
  63. data/webpack/src/Router/routes.js +30 -0
  64. data/webpack/test_setup.js +17 -0
  65. data/webpack/webpack.config.js +7 -0
  66. metadata +208 -0
data/config/routes.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ ForemanOpenbolt::Engine.routes.draw do
4
+ # React-rendered pages
5
+ get 'page_launch_task', to: 'task#page_launch_task', as: :page_launch_task
6
+ get 'page_task_execution', to: 'task#page_task_execution', as: :page_task_execution
7
+ get 'page_task_history', to: 'task#page_task_history', as: :page_task_history
8
+
9
+ # API endpoints
10
+ get 'fetch_tasks', to: 'task#fetch_tasks'
11
+ get 'reload_tasks', to: 'task#reload_tasks'
12
+ get 'fetch_openbolt_options', to: 'task#fetch_openbolt_options'
13
+ post 'launch_task', to: 'task#launch_task'
14
+ get 'job_status', to: 'task#job_status'
15
+ get 'job_result', to: 'task#job_result'
16
+
17
+ # Task job management endpoints
18
+ get 'fetch_task_history', to: 'task#fetch_task_history'
19
+ get 'fetch_task_history/:id', to: 'task#show', as: :task_job
20
+ end
21
+
22
+ Foreman::Application.routes.draw do
23
+ mount ForemanOpenbolt::Engine, at: '/foreman_openbolt'
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateOpenboltTaskJobs < ActiveRecord::Migration[6.1]
4
+ def change
5
+ create_table :openbolt_task_jobs, id: false do |t|
6
+ t.string :job_id, null: false, primary_key: true
7
+ t.references :smart_proxy, null: false, foreign_key: true, index: true
8
+ t.string :task_name, null: false
9
+ t.string :status, null: false, default: 'pending'
10
+
11
+ # JSON columns for complex data
12
+ t.jsonb :targets, default: []
13
+ t.jsonb :task_parameters, default: {}
14
+ t.jsonb :openbolt_options, default: {}
15
+ t.jsonb :result
16
+
17
+ t.text :log
18
+
19
+ t.datetime :submitted_at, null: false, index: true
20
+ t.datetime :completed_at
21
+
22
+ t.timestamps
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCommandToOpenboltTaskJobs < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :openbolt_task_jobs, :command, :string
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTaskDescriptionToTaskJobs < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :openbolt_task_jobs, :task_description, :text
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ f = Feature.where(name: 'OpenBolt').first_or_create
4
+ raise "Unable to create OpenBolt proxy feature: #{format_errors f}" if f.nil? || f.errors.any?
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanOpenbolt
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace ForemanOpenbolt
6
+ engine_name 'foreman_openbolt'
7
+
8
+ # Add any db migrations
9
+ initializer 'foreman_openbolt.load_app_instance_data' do |app|
10
+ ForemanOpenbolt::Engine.paths['db/migrate'].existent.each do |path|
11
+ app.config.paths['db/migrate'] << path
12
+ end
13
+ end
14
+
15
+ initializer 'foreman_openbolt.register_plugin', before: :finisher_hook do |app|
16
+ app.reloader.to_prepare do
17
+ Foreman::Plugin.register :foreman_openbolt do
18
+ requires_foreman '>= 3.14.0'
19
+ register_gettext
20
+
21
+ settings do
22
+ category :openbolt, N_('OpenBolt') do
23
+ # The setting name should be prefixed by 'openbolt_' and otherwise exactly
24
+ # match the OpenBolt option name defined in main.rb of smart_proxy_openbolt.
25
+ # At some point, we should figure out the best way to deduplicate this definition
26
+ # here and in the smart proxy.
27
+ # The proxy needs the information for validation and a custom field to tell
28
+ # the UI which settings apply to which transports, which we can't embed here
29
+ # easily. And the Foreman Settings UI needs these defined here on plugin load.
30
+ # Maybe the proxy can just hold a map of settings to transports and pull the
31
+ # settings from here. However, we'll need to figure out the best way of doing
32
+ # Proxy -> Foreman communication with the variety of auth methods people use.
33
+
34
+ # rubocop:disable Lint/ConstantDefinitionInBlock
35
+ TRANSPORTS = {
36
+ 'ssh': N_('SSH'),
37
+ 'winrm': N_('WinRM'),
38
+ }.freeze
39
+ LOG_LEVELS = {
40
+ 'error': N_('Error'),
41
+ 'warning': N_('Warning'),
42
+ 'info': N_('Info'),
43
+ 'debug': N_('Debug'),
44
+ 'trace': N_('Trace'),
45
+ }.freeze
46
+ # rubocop:enable Lint/ConstantDefinitionInBlock
47
+
48
+ setting 'openbolt_transport',
49
+ type: :string,
50
+ default: 'ssh',
51
+ full_name: N_('Transport'),
52
+ description: N_('The transport method to use for connecting to target hosts'),
53
+ collection: proc { TRANSPORTS }
54
+ setting 'openbolt_log-level',
55
+ type: :string,
56
+ default: 'debug',
57
+ full_name: N_('Log Level'),
58
+ description: N_('Set the log level during OpenBolt execution'),
59
+ collection: proc { LOG_LEVELS }
60
+ setting 'openbolt_verbose',
61
+ type: :boolean,
62
+ default: false,
63
+ full_name: N_('Verbose'),
64
+ description: N_(
65
+ 'Run the OpenBolt command with the --verbose flag. This prints additional information ' +
66
+ 'during OpenBolt execution and will print any out::verbose plan statements.'
67
+ )
68
+ setting 'openbolt_noop',
69
+ type: :boolean,
70
+ default: false,
71
+ full_name: N_('No Operation'),
72
+ description: N_(
73
+ 'Run the OpenBolt command with the --noop flag, which will make no changes to the target host'
74
+ )
75
+ setting 'openbolt_tmpdir',
76
+ type: :string,
77
+ default: '',
78
+ full_name: N_('Temporary Directory'),
79
+ description: N_('Directory to use for temporary files on target hosts during OpenBolt execution')
80
+ setting 'openbolt_user',
81
+ type: :string,
82
+ default: '',
83
+ full_name: N_('User'),
84
+ description: N_('Username used for SSH or WinRM authentication')
85
+ setting 'openbolt_password',
86
+ type: :string,
87
+ default: '',
88
+ full_name: N_('Password'),
89
+ description: N_('Password used for SSH or WinRM authentication'),
90
+ encrypted: true
91
+ setting 'openbolt_host-key-check',
92
+ type: :boolean,
93
+ default: true,
94
+ full_name: N_('SSH Host Key Check'),
95
+ description: N_('Whether to perform host key verification when connecting to targets over SSH')
96
+ setting 'openbolt_private-key',
97
+ type: :string,
98
+ default: '',
99
+ full_name: N_('SSH Private Key'),
100
+ description: N_(
101
+ 'Path on the smart proxy host to the private key used for SSH authentication. This key must be ' +
102
+ 'readable by the foreman-proxy user.'
103
+ )
104
+ setting 'openbolt_run-as',
105
+ type: :string,
106
+ default: '',
107
+ full_name: N_('SSH Run As User'),
108
+ description: N_(
109
+ 'The user to run commands as on the target host. This requires that the user specified ' +
110
+ 'in the "user" option has permission to run commands as this user.'
111
+ )
112
+ setting 'openbolt_sudo-password',
113
+ type: :string,
114
+ default: '',
115
+ full_name: N_('SSH Sudo Password'),
116
+ description: N_('Password used for privilege escalation when using SSH'),
117
+ encrypted: true
118
+ setting 'openbolt_ssl',
119
+ type: :boolean,
120
+ default: true,
121
+ full_name: N_('WinRM SSL'),
122
+ description: N_('Use SSL when connecting to hosts via WinRM')
123
+ setting 'openbolt_ssl-verify',
124
+ type: :boolean,
125
+ default: true,
126
+ full_name: N_('WinRM SSL Verify'),
127
+ description: N_('Verify remote host SSL certificate when connecting to hosts via WinRM')
128
+ end
129
+ end
130
+
131
+ # Right now, this is really only pulling in routes. But leaving
132
+ # it as a general global JS file for future expansion.
133
+ register_global_js_file 'global'
134
+
135
+ security_block :foreman_openbolt do
136
+ permission :execute_openbolt,
137
+ { :'foreman_openbolt/task' => [
138
+ :page_launch_task, :page_task_execution, :page_task_history,
139
+ :fetch_tasks, :reload_tasks, :fetch_openbolt_options,
140
+ :launch_task, :job_status, :job_result, :fetch_task_history, :show
141
+ ] }
142
+ permission :view_smart_proxies_openbolt, :smart_proxies => [:index, :show], :resource_type => 'SmartProxy'
143
+ end
144
+
145
+ role 'OpenBolt Executor', [:execute_openbolt]
146
+ add_all_permissions_to_default_roles
147
+
148
+ sub_menu :top_menu, :openbolt,
149
+ icon: 'fa fa-bolt',
150
+ caption: N_('OpenBolt'),
151
+ after: :hosts_menu do
152
+ menu :top_menu, :page_launch_task,
153
+ caption: N_('Launch Task'),
154
+ engine: ForemanOpenbolt::Engine
155
+ menu :top_menu, :page_task_history,
156
+ caption: N_('Task History'),
157
+ engine: ForemanOpenbolt::Engine
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ rake_tasks do
164
+ Rake::Task['db:seed'].enhance do
165
+ ForemanOpenbolt::Engine.load_seed
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanOpenbolt
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'foreman-tasks'
4
+ require 'foreman_openbolt/engine'
5
+
6
+ module ForemanOpenbolt
7
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module ProxyAPI
6
+ class Openbolt < Resource
7
+ def initialize(args)
8
+ @url = args[:url]
9
+ super args
10
+ end
11
+
12
+ def fetch_tasks
13
+ @tasks = JSON.parse(get('/openbolt/tasks').body)
14
+ end
15
+
16
+ def tasks
17
+ @tasks ||= fetch_tasks
18
+ end
19
+
20
+ def reload_tasks
21
+ @tasks = JSON.parse(get('/openbolt/tasks/reload').body)
22
+ end
23
+
24
+ def task_names
25
+ tasks.keys
26
+ end
27
+
28
+ def openbolt_options
29
+ @openbolt_options ||= JSON.parse(get('/openbolt/tasks/options').body)
30
+ end
31
+
32
+ def launch_task(name:, targets:, parameters: {}, options: {})
33
+ JSON.parse(post({
34
+ name: name,
35
+ targets: targets,
36
+ parameters: parameters,
37
+ options: options,
38
+ }.to_json, '/openbolt/launch/task').body)
39
+ end
40
+
41
+ def job_status(job_id:)
42
+ JSON.parse(get("/openbolt/job/#{job_id}/status").body)
43
+ end
44
+
45
+ def job_result(job_id:)
46
+ JSON.parse(get("/openbolt/job/#{job_id}/result").body)
47
+ end
48
+
49
+ def delete_job_artifacts(job_id:)
50
+ JSON.parse(delete("/openbolt/job/#{job_id}/artifacts").body)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+
5
+ # Tasks
6
+ namespace :openbolt do
7
+ desc 'Refresh smart proxy features to detect OpenBolt feature'
8
+ task refresh_proxies: :environment do
9
+ puts "Refreshing smart proxies to detect OpenBolt feature"
10
+ proxies = SmartProxy.unscoped
11
+ proxies.each do |proxy|
12
+ print "Refreshing #{proxy.name}... "
13
+ begin
14
+ proxy.refresh
15
+ proxy.reload
16
+ has_feature = proxy.features.map(&:name).include?('OpenBolt')
17
+ puts has_feature ? 'OpenBolt FOUND' : 'OpenBolt NOT FOUND'
18
+ rescue StandardError => e
19
+ puts "FAILED: #{e.message}"
20
+ end
21
+ end
22
+
23
+ if proxies.count.zero?
24
+ puts "No smart proxies found"
25
+ else
26
+ openbolt_count = proxies.with_features('OpenBolt').count
27
+ puts "Total proxies with OpenBolt: #{openbolt_count}/#{proxies.count}"
28
+ end
29
+ end
30
+ end
31
+
32
+ # Tests
33
+ namespace :test do
34
+ desc 'Test ForemanOpenbolt'
35
+ Rake::TestTask.new(:foreman_openbolt) do |t|
36
+ test_dir = File.expand_path('../../test', __dir__)
37
+ t.libs << 'test'
38
+ t.libs << test_dir
39
+ t.pattern = "#{test_dir}/**/*_test.rb"
40
+ t.verbose = true
41
+ t.warning = false
42
+ end
43
+ end
44
+
45
+ Rake::Task[:test].enhance ['test:foreman_openbolt']
46
+
47
+ load 'tasks/jenkins.rake'
48
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_openbolt', 'foreman_openbolt:rubocop'] if Rake::Task.task_defined?(:'jenkins:unit')
data/locale/Makefile ADDED
@@ -0,0 +1,73 @@
1
+ #
2
+ # Makefile for PO merging and MO generation. More info in the README.
3
+ #
4
+ # make all-mo (default) - generate MO files
5
+ # make check - check translations using translate-tool
6
+ # make tx-update - download and merge translations from Transifex
7
+ # make clean - clean everything
8
+ #
9
+ DOMAIN = $(shell ruby -rrubygems -e 'puts Gem::Specification::load(Dir.glob("../*.gemspec")[0]).name')
10
+ VERSION = $(shell ruby -rrubygems -e 'puts Gem::Specification::load(Dir.glob("../*.gemspec")[0]).version')
11
+ POTFILE = $(DOMAIN).pot
12
+ MOFILE = $(DOMAIN).mo
13
+ POFILES = $(shell find . -name '$(DOMAIN).po')
14
+ MOFILES = $(patsubst %.po,%.mo,$(POFILES))
15
+ POXFILES = $(patsubst %.po,%.pox,$(POFILES))
16
+ EDITFILES = $(patsubst %.po,%.edit.po,$(POFILES))
17
+ JSFILES = $(shell find ../app/assets/javascripts/*/locale -name '$(DOMAIN).js')
18
+
19
+ %.mo: %.po
20
+ mkdir -p $(shell dirname $@)/LC_MESSAGES
21
+ msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
22
+
23
+ # Generate MO files from PO files
24
+ all-mo: $(MOFILES)
25
+
26
+ # Check for malformed strings
27
+ %.pox: %.po
28
+ msgfmt -c $<
29
+ pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
30
+ -t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
31
+ cat $@
32
+ ! grep -q msgid $@
33
+
34
+ %.edit.po: %.po.time_stamp
35
+ touch $@
36
+
37
+ # gettext will trash the .edit.po file if the time stamp doesn't exist or is older than the po file
38
+ %.po.time_stamp: %.po
39
+ touch --reference $< $@
40
+
41
+ # Prevent make from treating this as an intermediate file to be cleaned up
42
+ .PRECIOUS: %.po.time_stamp
43
+
44
+ check: $(POXFILES)
45
+
46
+ # Unify duplicate translations
47
+ uniq-po:
48
+ for f in $(shell find ./ -name "*.po") ; do \
49
+ msguniq $$f -o $$f ; \
50
+ done
51
+
52
+ tx-pull: $(EDITFILES)
53
+ # Initialize new languages
54
+ cd .. && tx pull -f --all --minimum-perc 50
55
+ # Force update all existing languages
56
+ cd .. && tx pull -f --minimum-perc 0
57
+ for f in $(EDITFILES) ; do \
58
+ sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
59
+ done
60
+
61
+ tx-update: tx-pull
62
+ @echo
63
+ @echo Run rake plugin:gettext[$(DOMAIN)] from the Foreman installation
64
+ @echo then run rake plugin:po_to_json[$(DOMAIN)] from the Foreman installation
65
+ @echo then run make -C locale mo-files to finish
66
+ @echo
67
+
68
+ mo-files: $(MOFILES)
69
+ git add $(POFILES) $(POTFILE) $(JSFILES) ../locale/*/LC_MESSAGES
70
+ git commit -m "i18n - pulling from tx"
71
+ @echo
72
+ @echo Changes commited!
73
+ @echo
@@ -0,0 +1,19 @@
1
+ # foreman_openbolt
2
+ #
3
+ # This file is distributed under the same license as foreman_openbolt.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:54+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
+
@@ -0,0 +1,19 @@
1
+ # foreman_openbolt
2
+ #
3
+ # This file is distributed under the same license as foreman_openbolt.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:46+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19
+
data/locale/gemspec.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Matches foreman_openbolt.gemspec
4
+ _(
5
+ 'This plugin adds OpenBolt integration into Foreman, allowing users to run tasks ' +
6
+ 'present in their environment.'
7
+ )
data/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "foreman_openbolt",
3
+ "version": "0.0.1",
4
+ "description": "OpenBolt integration into Foreman",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "tfm-lint --plugin -d /webpack",
8
+ "test": "tfm-test --config jest.config.js",
9
+ "test:watch": "tfm-test --plugin --watchAll",
10
+ "test:current": "tfm-test --plugin --watch",
11
+ "publish-coverage": "tfm-publish-coverage",
12
+ "create-react-component": "yo react-domain"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/theforeman/foreman_openbolt.git"
17
+ },
18
+ "bugs": {
19
+ "url": "http://projects.theforeman.org/projects/foreman_openbolt/issues"
20
+ },
21
+ "peerDependencies": {
22
+ "@theforeman/vendor": ">= 6.0.0"
23
+ },
24
+ "dependencies": {
25
+ "react-intl": "^2.8.0"
26
+ },
27
+ "devDependencies": {
28
+ "@babel/core": "^7.7.0",
29
+ "@sheerun/mutationobserver-shim": "^0.3.3",
30
+ "@theforeman/builder": "^6.0.0",
31
+ "@theforeman/eslint-plugin-foreman": "6.0.0",
32
+ "@theforeman/find-foreman": "^4.8.0",
33
+ "@theforeman/test": "^8.0.0",
34
+ "@theforeman/vendor-dev": "^6.0.0",
35
+ "babel-eslint": "^10.0.3",
36
+ "eslint": "^6.7.2",
37
+ "prettier": "^1.19.1",
38
+ "stylelint-config-standard": "^18.0.0",
39
+ "stylelint": "^9.3.0"
40
+ }
41
+ }
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :host do
5
+ name 'foreman_openbolt'
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This calls the main test_helper in Foreman-core
4
+ require 'test_helper'
5
+
6
+ # Add plugin to FactoryBot's paths
7
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
+ FactoryBot.reload
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class ForemanOpenboltTest < ActiveSupport::TestCase
6
+ setup do
7
+ User.current = User.find_by login: 'admin'
8
+ end
9
+
10
+ test 'the truth' do
11
+ assert true
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ import { registerRoutes } from 'foremanReact/routes/RoutingService';
2
+ import Routes from './src/Router/routes';
3
+
4
+ registerRoutes('ForemanOpenbolt', Routes);
@@ -0,0 +1,11 @@
1
+ // runs before each test to make sure console.error output will
2
+ // fail a test (i.e. default PropType missing). Check the error
3
+ // output and traceback for actual error.
4
+ global.console.error = (error, stack) => {
5
+ /* eslint-disable-next-line no-console */
6
+ if (stack) console.log(stack); // Prints out original stack trace
7
+ throw new Error(error);
8
+ };
9
+
10
+ // Increase jest timeout as some tests using multiple http mocks can time out on CI systems.
11
+ jest.setTimeout(10000);
data/webpack/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import componentRegistry from 'foremanReact/components/componentRegistry';
2
+
3
+ import LaunchTask from './src/Components/LaunchTask';
4
+ import TaskExecution from './src/Components/TaskExecution';
5
+
6
+ const components = [
7
+ {
8
+ name: 'LaunchTask',
9
+ type: LaunchTask,
10
+ },
11
+ {
12
+ name: 'TaskExecution',
13
+ type: TaskExecution,
14
+ },
15
+ ];
16
+
17
+ components.forEach(component => {
18
+ componentRegistry.register(component);
19
+ });
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ EmptyState,
5
+ EmptyStateIcon,
6
+ EmptyStateHeader,
7
+ } from '@patternfly/react-core';
8
+ import { InfoCircleIcon } from '@patternfly/react-icons';
9
+
10
+ const EmptyContent = ({ title }) => (
11
+ <EmptyState>
12
+ <EmptyStateHeader
13
+ titleText={title}
14
+ icon={<EmptyStateIcon icon={InfoCircleIcon} />}
15
+ headingLevel="h4"
16
+ />
17
+ </EmptyState>
18
+ );
19
+
20
+ EmptyContent.propTypes = {
21
+ title: PropTypes.string.isRequired,
22
+ };
23
+
24
+ export default EmptyContent;