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.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +46 -0
- data/Rakefile +106 -0
- data/app/controllers/foreman_openbolt/task_controller.rb +298 -0
- data/app/lib/actions/foreman_openbolt/cleanup_proxy_artifacts.rb +40 -0
- data/app/lib/actions/foreman_openbolt/poll_task_status.rb +151 -0
- data/app/models/foreman_openbolt/task_job.rb +110 -0
- data/app/views/foreman_openbolt/react_page.html.erb +1 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20250819000000_create_openbolt_task_jobs.rb +25 -0
- data/db/migrate/20250925000000_add_command_to_openbolt_task_jobs.rb +7 -0
- data/db/migrate/20251001000000_add_task_description_to_task_jobs.rb +7 -0
- data/db/seeds.d/001_add_openbolt_feature.rb +4 -0
- data/lib/foreman_openbolt/engine.rb +169 -0
- data/lib/foreman_openbolt/version.rb +5 -0
- data/lib/foreman_openbolt.rb +7 -0
- data/lib/proxy_api/openbolt.rb +53 -0
- data/lib/tasks/foreman_openbolt_tasks.rake +48 -0
- data/locale/Makefile +73 -0
- data/locale/en/foreman_openbolt.po +19 -0
- data/locale/foreman_openbolt.pot +19 -0
- data/locale/gemspec.rb +7 -0
- data/package.json +41 -0
- data/test/factories/foreman_openbolt_factories.rb +7 -0
- data/test/test_plugin_helper.rb +8 -0
- data/test/unit/foreman_openbolt_test.rb +13 -0
- data/webpack/global_index.js +4 -0
- data/webpack/global_test_setup.js +11 -0
- data/webpack/index.js +19 -0
- data/webpack/src/Components/LaunchTask/EmptyContent.js +24 -0
- data/webpack/src/Components/LaunchTask/FieldTable.js +147 -0
- data/webpack/src/Components/LaunchTask/HostSelector/HostSearch.js +29 -0
- data/webpack/src/Components/LaunchTask/HostSelector/SearchSelect.js +208 -0
- data/webpack/src/Components/LaunchTask/HostSelector/SelectedChips.js +113 -0
- data/webpack/src/Components/LaunchTask/HostSelector/hostgroups.gql +9 -0
- data/webpack/src/Components/LaunchTask/HostSelector/hosts.gql +10 -0
- data/webpack/src/Components/LaunchTask/HostSelector/index.js +261 -0
- data/webpack/src/Components/LaunchTask/OpenBoltOptionsSection.js +116 -0
- data/webpack/src/Components/LaunchTask/ParameterField.js +145 -0
- data/webpack/src/Components/LaunchTask/ParametersSection.js +66 -0
- data/webpack/src/Components/LaunchTask/SmartProxySelect.js +51 -0
- data/webpack/src/Components/LaunchTask/TaskSelect.js +84 -0
- data/webpack/src/Components/LaunchTask/hooks/useOpenBoltOptions.js +63 -0
- data/webpack/src/Components/LaunchTask/hooks/useSmartProxies.js +48 -0
- data/webpack/src/Components/LaunchTask/hooks/useTasksData.js +64 -0
- data/webpack/src/Components/LaunchTask/index.js +333 -0
- data/webpack/src/Components/TaskExecution/ExecutionDetails.js +188 -0
- data/webpack/src/Components/TaskExecution/ExecutionDisplay.js +99 -0
- data/webpack/src/Components/TaskExecution/LoadingIndicator.js +51 -0
- data/webpack/src/Components/TaskExecution/ResultDisplay.js +174 -0
- data/webpack/src/Components/TaskExecution/TaskDetails.js +99 -0
- data/webpack/src/Components/TaskExecution/hooks/useJobPolling.js +142 -0
- data/webpack/src/Components/TaskExecution/index.js +130 -0
- data/webpack/src/Components/TaskHistory/TaskPopover.js +95 -0
- data/webpack/src/Components/TaskHistory/index.js +199 -0
- data/webpack/src/Components/common/HostsPopover.js +49 -0
- data/webpack/src/Components/common/constants.js +44 -0
- data/webpack/src/Components/common/helpers.js +19 -0
- data/webpack/src/Pages/LaunchTaskPage.js +12 -0
- data/webpack/src/Pages/TaskExecutionPage.js +12 -0
- data/webpack/src/Pages/TaskHistoryPage.js +12 -0
- data/webpack/src/Router/routes.js +30 -0
- data/webpack/test_setup.js +17 -0
- data/webpack/webpack.config.js +7 -0
- 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,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,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
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,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;
|