foreman_remote_execution_core 1.0.2 → 1.0.3
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/lib/foreman_remote_execution_core.rb +14 -1
- data/lib/foreman_remote_execution_core/actions.rb +1 -1
- data/lib/foreman_remote_execution_core/fake_script_runner.rb +82 -0
- data/lib/foreman_remote_execution_core/script_runner.rb +38 -23
- data/lib/foreman_remote_execution_core/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3216a936b41179b58d8ae81a238d2fbdaff11e56
|
4
|
+
data.tar.gz: aec1f9700b5a9f7daa4fbe6259dc8e2d64943b30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2eadacdd4acfd582061150991734a46ac53909874379c0cf7f4562089aa32342511875773c84134c97f2e2910ae9bd94aeb21c6726c0a7652dc93ccdb808a282
|
7
|
+
data.tar.gz: 6f6666aedd9474440e52a15f2927e5e279e439e75fb475e8bb9b747b921d83cb003a978b50d80596858845c06d581c51e68d59f1fc75697264a4e062cc8a8490
|
@@ -8,9 +8,22 @@ module ForemanRemoteExecutionCore
|
|
8
8
|
:remote_working_dir => '/var/tmp',
|
9
9
|
:local_working_dir => '/var/tmp')
|
10
10
|
|
11
|
+
def self.simulate?
|
12
|
+
%w(yes true 1).include? ENV.fetch('REX_SIMULATE', '').downcase
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.runner_class
|
16
|
+
@runner_class ||= simulate? ? FakeScriptRunner : ScriptRunner
|
17
|
+
end
|
18
|
+
|
11
19
|
if ForemanTasksCore.dynflow_present?
|
12
20
|
require 'foreman_tasks_core/runner'
|
13
|
-
|
21
|
+
if simulate?
|
22
|
+
# Load the fake implementation of the script runner if debug is enabled
|
23
|
+
require 'foreman_remote_execution_core/fake_script_runner'
|
24
|
+
else
|
25
|
+
require 'foreman_remote_execution_core/script_runner'
|
26
|
+
end
|
14
27
|
require 'foreman_remote_execution_core/actions'
|
15
28
|
end
|
16
29
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ForemanRemoteExecutionCore
|
2
|
+
class FakeScriptRunner < ForemanTasksCore::Runner::Base
|
3
|
+
|
4
|
+
@data = []
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :data
|
8
|
+
|
9
|
+
def load_data(path = nil)
|
10
|
+
if path.nil?
|
11
|
+
@data = <<-END.gsub(/^\s+\| ?/, '').lines
|
12
|
+
| ====== Simulated Remote Execution ======
|
13
|
+
|
|
14
|
+
| This is an output of a simulated remote
|
15
|
+
| execution run. It should run for about
|
16
|
+
| 5 seconds and finish successfully.
|
17
|
+
END
|
18
|
+
else
|
19
|
+
File.open(File.expand_path(path), 'r') do |f|
|
20
|
+
@data = f.readlines.map(&:chomp)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@data.freeze
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(*args)
|
28
|
+
super
|
29
|
+
# Load the fake output the first time its needed
|
30
|
+
self.class.load_data(ENV['REX_SIMULATE_PATH']) unless self.class.data.frozen?
|
31
|
+
@position = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def start
|
35
|
+
refresh
|
36
|
+
end
|
37
|
+
|
38
|
+
# Do one step
|
39
|
+
def refresh
|
40
|
+
if done?
|
41
|
+
finish
|
42
|
+
else
|
43
|
+
step
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def kill
|
48
|
+
finish
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def finish
|
54
|
+
publish_exit_status exit_code
|
55
|
+
end
|
56
|
+
|
57
|
+
def step
|
58
|
+
publish_data(next_chunk, 'stdout')
|
59
|
+
end
|
60
|
+
|
61
|
+
def done?
|
62
|
+
@position == self.class.data.count
|
63
|
+
end
|
64
|
+
|
65
|
+
def next_chunk
|
66
|
+
output = self.class.data[@position]
|
67
|
+
@position += 1
|
68
|
+
output
|
69
|
+
end
|
70
|
+
|
71
|
+
# Decide if the execution should fail or not
|
72
|
+
def exit_code
|
73
|
+
fail_chance = ENV.fetch('REX_SIMULATE_FAIL_CHANCE', 0).to_i
|
74
|
+
fail_exitcode = ENV.fetch('REX_SIMULATE_EXIT', 0)
|
75
|
+
if fail_exitcode == 0 || fail_chance < (Random.rand * 100).round
|
76
|
+
0
|
77
|
+
else
|
78
|
+
fail_exitcode
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'net/ssh'
|
2
|
-
require 'net/scp'
|
3
2
|
|
4
3
|
module ForemanRemoteExecutionCore
|
5
4
|
class ScriptRunner < ForemanTasksCore::Runner::Base
|
@@ -28,7 +27,7 @@ module ForemanRemoteExecutionCore
|
|
28
27
|
|
29
28
|
# pipe the output to tee while capturing the exit code in a file
|
30
29
|
script = <<-SCRIPT
|
31
|
-
(#{su_prefix}#{remote_script}; echo $?>#{exit_code_path}) | /usr/bin/tee #{output_path}
|
30
|
+
(#{su_prefix}#{remote_script} < /dev/null; echo $?>#{exit_code_path}) | /usr/bin/tee #{output_path}
|
32
31
|
exit $(< #{exit_code_path})
|
33
32
|
SCRIPT
|
34
33
|
|
@@ -148,13 +147,18 @@ module ForemanRemoteExecutionCore
|
|
148
147
|
return true
|
149
148
|
end
|
150
149
|
|
151
|
-
def run_sync(command)
|
152
|
-
|
150
|
+
def run_sync(command, stdin = nil)
|
151
|
+
stdout = ""
|
152
|
+
stderr = ""
|
153
153
|
exit_status = nil
|
154
|
+
started = false
|
155
|
+
|
154
156
|
channel = session.open_channel do |ch|
|
155
|
-
ch.on_data { |data|
|
156
|
-
ch.on_extended_data { |_, _, data|
|
157
|
+
ch.on_data { |_, data| stdout.concat(data) }
|
158
|
+
ch.on_extended_data { |_, _, data| stderr.concat(data) }
|
157
159
|
ch.on_request("exit-status") { |_, data| exit_status = data.read_long }
|
160
|
+
# Send data to stdin if we have some
|
161
|
+
ch.send_data(stdin) unless stdin.nil?
|
158
162
|
# on signal: sending the signal value (such as 'TERM')
|
159
163
|
ch.on_request("exit-signal") do |_, data|
|
160
164
|
exit_status = data.read_string
|
@@ -163,10 +167,14 @@ module ForemanRemoteExecutionCore
|
|
163
167
|
end
|
164
168
|
ch.exec command do |_, success|
|
165
169
|
raise "could not execute command" unless success
|
170
|
+
started = true
|
166
171
|
end
|
167
172
|
end
|
173
|
+
session.process(0) until started
|
174
|
+
# Closing the channel without sending any data gives us SIGPIPE
|
175
|
+
channel.close unless stdin.nil?
|
168
176
|
channel.wait
|
169
|
-
return exit_status,
|
177
|
+
return exit_status, stdout, stderr
|
170
178
|
end
|
171
179
|
|
172
180
|
def su_prefix
|
@@ -215,29 +223,36 @@ module ForemanRemoteExecutionCore
|
|
215
223
|
end
|
216
224
|
|
217
225
|
def cp_script_to_remote
|
218
|
-
|
219
|
-
File.chmod(0555, local_script_file)
|
220
|
-
remote_script_file = remote_command_file('script')
|
221
|
-
upload_file(local_script_file, remote_script_file)
|
222
|
-
return remote_script_file
|
226
|
+
upload_data(sanitize_script(@script), remote_command_file('script'), 555)
|
223
227
|
end
|
224
228
|
|
225
|
-
def
|
226
|
-
ensure_remote_directory
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
229
|
+
def upload_data(data, path, permissions = 555)
|
230
|
+
ensure_remote_directory File.dirname(path)
|
231
|
+
# We use tee here to pipe stdin coming from ssh to a file at $path, while silencing its output
|
232
|
+
# This is used to write to $path with elevated permissions, solutions using cat and output redirection
|
233
|
+
# would not work, because the redirection would happen in the non-elevated shell.
|
234
|
+
command = "#{su_prefix} tee '#{path}' >/dev/null && #{su_prefix} chmod '#{permissions}' '#{path}'"
|
235
|
+
|
236
|
+
@logger.debug("Sending data to #{path} on remote host:\n#{data}")
|
237
|
+
status, _out, err = run_sync(command, data)
|
238
|
+
|
239
|
+
@logger.warn("Output on stderr while uploading #{path}:\n#{err}") unless err.empty?
|
240
|
+
if status != 0
|
241
|
+
raise "Unable to upload file to #{path} on remote system: exit code: #{status}"
|
234
242
|
end
|
243
|
+
path
|
244
|
+
end
|
245
|
+
|
246
|
+
def upload_file(local_path, remote_path)
|
247
|
+
mode = File.stat(local_path).mode.to_s(8)[-3..-1]
|
248
|
+
@logger.debug('Uploading local file: #{local_path} as #{remote_path} with #{mode} permissions')
|
249
|
+
upload_data(File.read(local_path), remote_path, mode)
|
235
250
|
end
|
236
251
|
|
237
252
|
def ensure_remote_directory(path)
|
238
|
-
exit_code,
|
253
|
+
exit_code, _output, err = run_sync("mkdir -p #{path}")
|
239
254
|
if exit_code != 0
|
240
|
-
raise "Unable to create directory on remote system #{path}: exit code: #{exit_code}\n #{
|
255
|
+
raise "Unable to create directory on remote system #{path}: exit code: #{exit_code}\n #{err}"
|
241
256
|
end
|
242
257
|
end
|
243
258
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: foreman-tasks-core
|
@@ -52,8 +52,7 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description:
|
56
|
-
Ssh remote execution provider code sharable between Foreman and Foreman-Proxy
|
55
|
+
description: " Ssh remote execution provider code sharable between Foreman and Foreman-Proxy\n"
|
57
56
|
email:
|
58
57
|
- inecas@redhat.com
|
59
58
|
executables: []
|
@@ -63,6 +62,7 @@ files:
|
|
63
62
|
- LICENSE
|
64
63
|
- lib/foreman_remote_execution_core.rb
|
65
64
|
- lib/foreman_remote_execution_core/actions.rb
|
65
|
+
- lib/foreman_remote_execution_core/fake_script_runner.rb
|
66
66
|
- lib/foreman_remote_execution_core/script_runner.rb
|
67
67
|
- lib/foreman_remote_execution_core/version.rb
|
68
68
|
homepage: https://github.com/theforeman/foreman_remote_execution
|
@@ -85,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
85
|
version: '0'
|
86
86
|
requirements: []
|
87
87
|
rubyforge_project:
|
88
|
-
rubygems_version: 2.
|
88
|
+
rubygems_version: 2.5.1
|
89
89
|
signing_key:
|
90
90
|
specification_version: 4
|
91
91
|
summary: Foreman remote execution - core bits
|