smart_proxy_remote_execution_ssh 0.3.2 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{bundler.plugins.d → bundler.d}/remote_execution_ssh.rb +0 -0
- data/lib/smart_proxy_remote_execution_ssh/actions/pull_script.rb +110 -0
- data/lib/smart_proxy_remote_execution_ssh/actions/run_script.rb +34 -0
- data/lib/smart_proxy_remote_execution_ssh/actions.rb +6 -0
- data/lib/smart_proxy_remote_execution_ssh/api.rb +49 -0
- data/lib/smart_proxy_remote_execution_ssh/async_scripts/control.sh +110 -0
- data/lib/smart_proxy_remote_execution_ssh/async_scripts/retrieve.sh +151 -0
- data/lib/smart_proxy_remote_execution_ssh/cockpit.rb +87 -71
- data/lib/smart_proxy_remote_execution_ssh/dispatcher.rb +10 -0
- data/lib/smart_proxy_remote_execution_ssh/job_storage.rb +51 -0
- data/lib/smart_proxy_remote_execution_ssh/log_filter.rb +14 -0
- data/lib/smart_proxy_remote_execution_ssh/net_ssh_compat.rb +228 -0
- data/lib/smart_proxy_remote_execution_ssh/plugin.rb +32 -10
- data/lib/smart_proxy_remote_execution_ssh/runners/fake_script_runner.rb +87 -0
- data/lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb +139 -0
- data/lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb +420 -0
- data/lib/smart_proxy_remote_execution_ssh/runners.rb +7 -0
- data/lib/smart_proxy_remote_execution_ssh/utils.rb +24 -0
- data/lib/smart_proxy_remote_execution_ssh/version.rb +1 -1
- data/lib/smart_proxy_remote_execution_ssh.rb +60 -2
- data/settings.d/remote_execution_ssh.yml.example +12 -3
- metadata +33 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2515f08b67e5b567e50182d278d73d2db8466e7a12cb6e8000ad8e4f8ce6bf65
|
4
|
+
data.tar.gz: 686ba55bf862fc81f3154ebadbaafa3702416109e81641bdcb2442873c5a451b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f9e84c78cab2997b711ee559440ad9323c6c3605726f6360a94415ccda3f7a08db0450fba8fe5479e06b649a8e6e8e545be72f35751762c50bf8ef5bc32696e
|
7
|
+
data.tar.gz: 0705716e01985cd8b5a3aa2ffd4104f6d4ce0938dc8a0e9b105f65f149d62c1c030916250cf0f1c35ed51bfaceb36964d0b959e5e13f42c70fb0a42389a01a2e
|
File without changes
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'mqtt'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Proxy::RemoteExecution::Ssh::Actions
|
5
|
+
class PullScript < Proxy::Dynflow::Action::Runner
|
6
|
+
JobDelivered = Class.new
|
7
|
+
|
8
|
+
execution_plan_hooks.use :cleanup, :on => :stopped
|
9
|
+
|
10
|
+
def plan(action_input, mqtt: false)
|
11
|
+
super(action_input)
|
12
|
+
input[:with_mqtt] = mqtt
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(event = nil)
|
16
|
+
if event == JobDelivered
|
17
|
+
output[:state] = :delivered
|
18
|
+
suspend
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def init_run
|
25
|
+
otp_password = if input[:with_mqtt]
|
26
|
+
::Proxy::Dynflow::OtpManager.generate_otp(execution_plan_id)
|
27
|
+
end
|
28
|
+
input[:job_uuid] = job_storage.store_job(host_name, execution_plan_id, run_step_id, input[:script])
|
29
|
+
output[:state] = :ready_for_pickup
|
30
|
+
output[:result] = []
|
31
|
+
mqtt_start(otp_password) if input[:with_mqtt]
|
32
|
+
suspend
|
33
|
+
end
|
34
|
+
|
35
|
+
def cleanup(_plan = nil)
|
36
|
+
job_storage.drop_job(execution_plan_id, run_step_id)
|
37
|
+
Proxy::Dynflow::OtpManager.passwords.delete(execution_plan_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_external_event(event)
|
41
|
+
output[:state] = :running
|
42
|
+
data = event.data
|
43
|
+
continuous_output = Proxy::Dynflow::ContinuousOutput.new
|
44
|
+
Array(data['output']).each { |line| continuous_output.add_output(line, 'stdout') } if data.key?('output')
|
45
|
+
exit_code = data['exit_code'].to_i if data['exit_code']
|
46
|
+
process_update(Proxy::Dynflow::Runner::Update.new(continuous_output, exit_code))
|
47
|
+
end
|
48
|
+
|
49
|
+
def kill_run
|
50
|
+
case output[:state]
|
51
|
+
when :ready_for_pickup
|
52
|
+
# If the job is not running yet on the client, wipe it from storage
|
53
|
+
cleanup
|
54
|
+
# TODO: Stop the action
|
55
|
+
when :notified, :running
|
56
|
+
# Client was notified or is already running, dealing with this situation
|
57
|
+
# is only supported if mqtt is available
|
58
|
+
# Otherwise we have to wait it out
|
59
|
+
# TODO
|
60
|
+
# if input[:with_mqtt]
|
61
|
+
end
|
62
|
+
suspend
|
63
|
+
end
|
64
|
+
|
65
|
+
def mqtt_start(otp_password)
|
66
|
+
payload = {
|
67
|
+
type: 'data',
|
68
|
+
message_id: SecureRandom.uuid,
|
69
|
+
version: 1,
|
70
|
+
sent: DateTime.now.iso8601,
|
71
|
+
directive: 'foreman',
|
72
|
+
metadata: {
|
73
|
+
'job_uuid': input[:job_uuid],
|
74
|
+
'username': execution_plan_id,
|
75
|
+
'password': otp_password,
|
76
|
+
'return_url': "#{input[:proxy_url]}/ssh/jobs/#{input[:job_uuid]}/update",
|
77
|
+
},
|
78
|
+
content: "#{input[:proxy_url]}/ssh/jobs/#{input[:job_uuid]}",
|
79
|
+
}
|
80
|
+
mqtt_notify payload
|
81
|
+
output[:state] = :notified
|
82
|
+
end
|
83
|
+
|
84
|
+
def mqtt_notify(payload)
|
85
|
+
MQTT::Client.connect(settings.mqtt_broker, settings.mqtt_port) do |c|
|
86
|
+
c.publish(mqtt_topic, JSON.dump(payload), false, 1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def host_name
|
91
|
+
alternative_names = input.fetch(:alternative_names, {})
|
92
|
+
|
93
|
+
alternative_names[:consumer_uuid] ||
|
94
|
+
alternative_names[:fqdn] ||
|
95
|
+
input[:hostname]
|
96
|
+
end
|
97
|
+
|
98
|
+
def mqtt_topic
|
99
|
+
"yggdrasil/#{host_name}/data/in"
|
100
|
+
end
|
101
|
+
|
102
|
+
def settings
|
103
|
+
Proxy::RemoteExecution::Ssh::Plugin.settings
|
104
|
+
end
|
105
|
+
|
106
|
+
def job_storage
|
107
|
+
Proxy::RemoteExecution::Ssh.job_storage
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'smart_proxy_dynflow/action/shareable'
|
2
|
+
require 'smart_proxy_dynflow/action/runner'
|
3
|
+
|
4
|
+
module Proxy::RemoteExecution::Ssh
|
5
|
+
module Actions
|
6
|
+
class RunScript < ::Dynflow::Action
|
7
|
+
def plan(*args)
|
8
|
+
mode = Proxy::RemoteExecution::Ssh::Plugin.settings.mode
|
9
|
+
case mode
|
10
|
+
when :ssh, :'ssh-async'
|
11
|
+
plan_action(ScriptRunner, *args)
|
12
|
+
when :pull, :'pull-mqtt'
|
13
|
+
plan_action(PullScript, *args,
|
14
|
+
mqtt: mode == :'pull-mqtt')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ScriptRunner < Proxy::Dynflow::Action::Runner
|
20
|
+
def initiate_runner
|
21
|
+
additional_options = {
|
22
|
+
:step_id => run_step_id,
|
23
|
+
:uuid => execution_plan_id,
|
24
|
+
}
|
25
|
+
Proxy::RemoteExecution::Ssh::Plugin.runner_class.build(input.merge(additional_options),
|
26
|
+
suspended_action: suspended_action)
|
27
|
+
end
|
28
|
+
|
29
|
+
def runner_dispatcher
|
30
|
+
Dispatcher.instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'net/ssh'
|
2
2
|
require 'base64'
|
3
|
+
require 'smart_proxy_dynflow/runner'
|
3
4
|
|
4
5
|
module Proxy::RemoteExecution
|
5
6
|
module Ssh
|
6
7
|
|
7
8
|
class Api < ::Sinatra::Base
|
8
9
|
include Sinatra::Authorization::Helpers
|
10
|
+
include Proxy::Dynflow::Helpers
|
9
11
|
|
10
12
|
get "/pubkey" do
|
11
13
|
File.read(Ssh.public_key_file)
|
@@ -37,6 +39,53 @@ module Proxy::RemoteExecution
|
|
37
39
|
end
|
38
40
|
204
|
39
41
|
end
|
42
|
+
|
43
|
+
# Payload is a hash where
|
44
|
+
# exit_code: Integer | NilClass
|
45
|
+
# output: String
|
46
|
+
post '/jobs/:job_uuid/update' do |job_uuid|
|
47
|
+
do_authorize_with_ssl_client
|
48
|
+
|
49
|
+
with_authorized_job(job_uuid) do |job_record|
|
50
|
+
data = MultiJson.load(request.body.read)
|
51
|
+
notify_job(job_record, ::Proxy::Dynflow::Runner::ExternalEvent.new(data))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/jobs' do
|
56
|
+
do_authorize_with_ssl_client
|
57
|
+
|
58
|
+
MultiJson.dump(Proxy::RemoteExecution::Ssh.job_storage.job_uuids_for_host(https_cert_cn))
|
59
|
+
end
|
60
|
+
|
61
|
+
get "/jobs/:job_uuid" do |job_uuid|
|
62
|
+
do_authorize_with_ssl_client
|
63
|
+
|
64
|
+
with_authorized_job(job_uuid) do |job_record|
|
65
|
+
notify_job(job_record, Actions::PullScript::JobDelivered)
|
66
|
+
job_record[:job]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def notify_job(job_record, event)
|
73
|
+
world.event(job_record[:execution_plan_uuid], job_record[:run_step_id], event)
|
74
|
+
end
|
75
|
+
|
76
|
+
def with_authorized_job(uuid)
|
77
|
+
if (job = authorized_job(uuid))
|
78
|
+
yield job
|
79
|
+
else
|
80
|
+
halt 404
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def authorized_job(uuid)
|
85
|
+
job_record = Proxy::RemoteExecution::Ssh.job_storage.find_job(uuid) || {}
|
86
|
+
return job_record if authorize_with_token(clear: false, task_id: job_record[:execution_plan_uuid]) ||
|
87
|
+
job_record[:hostname] == https_cert_cn
|
88
|
+
end
|
40
89
|
end
|
41
90
|
end
|
42
91
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# Control script for the remote execution jobs.
|
4
|
+
#
|
5
|
+
# The initial script calls `$CONTROL_SCRIPT init-script-finish` once the original script exits.
|
6
|
+
# In automatic mode, the exit code is sent back to the proxy on `init-script-finish`.
|
7
|
+
#
|
8
|
+
# What the script provides is also a manual mode, where the author of the rex script can take
|
9
|
+
# full control of the job lifecycle. This allows keeping the marked as running even when
|
10
|
+
# the initial script finishes.
|
11
|
+
#
|
12
|
+
# The manual mode is turned on by calling `$CONTROL_SCRIPT manual-control`. After calling this,
|
13
|
+
# one can call `echo message | $CONTROL_SCRIPT update` to send output to the remote execution jobs
|
14
|
+
# and `$CONTROL_SCRIPT finish 0` once finished (with 0 as exit code) to send output to the remote execution jobs
|
15
|
+
# and `$CONTROL_SCRIPT finish 0` once finished (with 0 as exit code)
|
16
|
+
BASE_DIR="$(dirname "$(readlink -f "$0")")"
|
17
|
+
|
18
|
+
if ! command -v curl >/dev/null; then
|
19
|
+
echo 'curl is required' >&2
|
20
|
+
exit 1
|
21
|
+
fi
|
22
|
+
|
23
|
+
# send the callback data to proxy
|
24
|
+
update() {
|
25
|
+
"$BASE_DIR/retrieve.sh" push_update
|
26
|
+
}
|
27
|
+
|
28
|
+
# wait for named pipe $1 to retrieve data. If $2 is provided, it serves as timeout
|
29
|
+
# in seconds on how long to wait when reading.
|
30
|
+
wait_for_pipe() {
|
31
|
+
pipe_path=$1
|
32
|
+
if [ -n "$2" ]; then
|
33
|
+
timeout="-t $2"
|
34
|
+
fi
|
35
|
+
if read $timeout <>"$pipe_path"; then
|
36
|
+
rm "$pipe_path"
|
37
|
+
return 0
|
38
|
+
else
|
39
|
+
return 1
|
40
|
+
fi
|
41
|
+
}
|
42
|
+
|
43
|
+
# function run in background, when receiving update data via STDIN.
|
44
|
+
periodic_update() {
|
45
|
+
interval=1
|
46
|
+
# reading some data from periodic_update_control signals we're done
|
47
|
+
while ! wait_for_pipe "$BASE_DIR/periodic_update_control" "$interval"; do
|
48
|
+
update
|
49
|
+
done
|
50
|
+
# one more update before we finish
|
51
|
+
update
|
52
|
+
# signal the main process that we are finished
|
53
|
+
echo > "$BASE_DIR/periodic_update_finished"
|
54
|
+
}
|
55
|
+
|
56
|
+
# signal the periodic_update process that the main process is finishing
|
57
|
+
periodic_update_finish() {
|
58
|
+
if [ -e "$BASE_DIR/periodic_update_control" ]; then
|
59
|
+
echo > "$BASE_DIR/periodic_update_control"
|
60
|
+
fi
|
61
|
+
}
|
62
|
+
|
63
|
+
ACTION=${1:-finish}
|
64
|
+
|
65
|
+
case "$ACTION" in
|
66
|
+
init-script-finish)
|
67
|
+
if ! [ -e "$BASE_DIR/manual_mode" ]; then
|
68
|
+
# make the exit code of initialization script the exit code of the whole job
|
69
|
+
cp init_exit_code exit_code
|
70
|
+
update
|
71
|
+
fi
|
72
|
+
;;
|
73
|
+
finish)
|
74
|
+
# take exit code passed via the command line, with fallback
|
75
|
+
# to the exit code of the initialization script
|
76
|
+
exit_code=${2:-$(cat "$BASE_DIR/init_exit_code")}
|
77
|
+
echo $exit_code > "$BASE_DIR/exit_code"
|
78
|
+
update
|
79
|
+
if [ -e "$BASE_DIR/manual_mode" ]; then
|
80
|
+
rm "$BASE_DIR/manual_mode"
|
81
|
+
fi
|
82
|
+
;;
|
83
|
+
update)
|
84
|
+
# read data from input when redirected though a pipe
|
85
|
+
if ! [ -t 0 ]; then
|
86
|
+
# couple of named pipes to coordinate the main process with the periodic_update
|
87
|
+
mkfifo "$BASE_DIR/periodic_update_control"
|
88
|
+
mkfifo "$BASE_DIR/periodic_update_finished"
|
89
|
+
trap "periodic_update_finish" EXIT
|
90
|
+
# run periodic update as separate process to keep sending updates in output to server
|
91
|
+
periodic_update &
|
92
|
+
# redirect the input into output
|
93
|
+
tee -a "$BASE_DIR/output"
|
94
|
+
periodic_update_finish
|
95
|
+
# ensure the periodic update finished before we return
|
96
|
+
wait_for_pipe "$BASE_DIR/periodic_update_finished"
|
97
|
+
else
|
98
|
+
update
|
99
|
+
fi
|
100
|
+
;;
|
101
|
+
# mark the script to be in manual mode: this means the script author needs to use `update` and `finish`
|
102
|
+
# commands to send output to the remote execution job or mark it as finished.
|
103
|
+
manual-mode)
|
104
|
+
touch "$BASE_DIR/manual_mode"
|
105
|
+
;;
|
106
|
+
*)
|
107
|
+
echo "Unknown action $ACTION"
|
108
|
+
exit 1
|
109
|
+
;;
|
110
|
+
esac
|
@@ -0,0 +1,151 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
if ! pgrep --help 2>/dev/null >/dev/null; then
|
4
|
+
echo DONE 1
|
5
|
+
echo "pgrep is required" >&2
|
6
|
+
exit 1
|
7
|
+
fi
|
8
|
+
|
9
|
+
BASE_DIR="$(dirname "$(readlink -f "$0")")"
|
10
|
+
|
11
|
+
# load the data required for generating the callback
|
12
|
+
. "$BASE_DIR/env.sh"
|
13
|
+
URL_PREFIX="$CALLBACK_HOST/dynflow/tasks/$TASK_ID"
|
14
|
+
AUTH="$TASK_ID:$OTP"
|
15
|
+
CURL="curl --silent --show-error --fail --max-time 10"
|
16
|
+
|
17
|
+
MY_LOCK_FILE="$BASE_DIR/retrieve_lock.$$"
|
18
|
+
MY_PID=$$
|
19
|
+
echo $MY_PID >"$MY_LOCK_FILE"
|
20
|
+
LOCK_FILE="$BASE_DIR/retrieve_lock"
|
21
|
+
TMP_OUTPUT_FILE="$BASE_DIR/tmp_output"
|
22
|
+
|
23
|
+
RUN_TIMEOUT=30 # for how long can the script hold the lock
|
24
|
+
WAIT_TIMEOUT=60 # for how long the script is trying to acquire the lock
|
25
|
+
START_TIME=$(date +%s)
|
26
|
+
|
27
|
+
fail() {
|
28
|
+
echo RUNNING
|
29
|
+
echo "$1"
|
30
|
+
exit 1
|
31
|
+
}
|
32
|
+
|
33
|
+
acquire_lock() {
|
34
|
+
# try to acquire lock by creating the file (ln should be atomic an fail in case
|
35
|
+
# another process succeeded first). We also check the content of the lock file,
|
36
|
+
# in case our process won when competing over the lock while invalidating
|
37
|
+
# the lock on timeout.
|
38
|
+
ln "$MY_LOCK_FILE" "$LOCK_FILE" 2>/dev/null || [ "$(head -n1 "$LOCK_FILE")" = "$MY_PID" ]
|
39
|
+
return $?
|
40
|
+
}
|
41
|
+
|
42
|
+
# acquiring the lock before proceeding, to ensure only one instance of the script is running
|
43
|
+
while ! acquire_lock; do
|
44
|
+
# we failed to create retrieve_lock - assuming there is already another retrieve script running
|
45
|
+
current_pid=$(head -n1 "$LOCK_FILE")
|
46
|
+
if [ -z "$current_pid" ]; then
|
47
|
+
continue
|
48
|
+
fi
|
49
|
+
# check whether the lock is not too old (compared to $RUN_TIMEOUT) and try to kill
|
50
|
+
# if it is, so that we don't have a stalled processes here
|
51
|
+
lock_lines_count=$(wc -l < "$LOCK_FILE")
|
52
|
+
current_lock_time=$(stat --format "%Y" "$LOCK_FILE")
|
53
|
+
current_time=$(date +%s)
|
54
|
+
|
55
|
+
if [ "$(( current_time - START_TIME ))" -gt "$WAIT_TIMEOUT" ]; then
|
56
|
+
# We were waiting for the lock for too long - just give up
|
57
|
+
fail "Wait time exceeded $WAIT_TIMEOUT"
|
58
|
+
elif [ "$(( current_time - current_lock_time ))" -gt "$RUN_TIMEOUT" ]; then
|
59
|
+
# The previous lock it hold for too long - re-acquiring procedure
|
60
|
+
if [ "$lock_lines_count" -gt 1 ]; then
|
61
|
+
# there were multiple processes waiting for lock without resolution
|
62
|
+
# longer than the $RUN_TIMEOUT - we reset the lock file and let processes
|
63
|
+
# to compete
|
64
|
+
echo "RETRY" > "$LOCK_FILE"
|
65
|
+
fi
|
66
|
+
if [ "$current_pid" != "RETRY" ]; then
|
67
|
+
# try to kill the currently stalled process
|
68
|
+
kill -9 "$current_pid" 2>/dev/null
|
69
|
+
fi
|
70
|
+
# try to add our process as one candidate
|
71
|
+
echo $MY_PID >> "$LOCK_FILE"
|
72
|
+
if [ "$( head -n2 "$LOCK_FILE" | tail -n1 )" = "$MY_PID" ]; then
|
73
|
+
# our process won the competition for the new lock: it is the first pid
|
74
|
+
# after the original one in the lock file - take ownership of the lock
|
75
|
+
# next iteration only this process will get through
|
76
|
+
echo $MY_PID >"$LOCK_FILE"
|
77
|
+
fi
|
78
|
+
else
|
79
|
+
# still waiting for the original owner to finish
|
80
|
+
sleep 1
|
81
|
+
fi
|
82
|
+
done
|
83
|
+
|
84
|
+
release_lock() {
|
85
|
+
rm "$MY_LOCK_FILE"
|
86
|
+
rm "$LOCK_FILE"
|
87
|
+
}
|
88
|
+
# ensure the release the lock at exit
|
89
|
+
trap "release_lock" EXIT
|
90
|
+
|
91
|
+
# make sure we clear previous tmp output file
|
92
|
+
if [ -e "$TMP_OUTPUT_FILE" ]; then
|
93
|
+
rm "$TMP_OUTPUT_FILE"
|
94
|
+
fi
|
95
|
+
|
96
|
+
pid=$(cat "$BASE_DIR/pid")
|
97
|
+
[ -f "$BASE_DIR/position" ] || echo 1 > "$BASE_DIR/position"
|
98
|
+
position=$(cat "$BASE_DIR/position")
|
99
|
+
|
100
|
+
prepare_output() {
|
101
|
+
if [ -e "$BASE_DIR/manual_mode" ] || ([ -n "$pid" ] && pgrep -P "$pid" >/dev/null 2>&1); then
|
102
|
+
echo RUNNING
|
103
|
+
else
|
104
|
+
echo "DONE $(cat "$BASE_DIR/exit_code" 2>/dev/null)"
|
105
|
+
fi
|
106
|
+
[ -f "$BASE_DIR/output" ] || exit 0
|
107
|
+
tail --bytes "+${position}" "$BASE_DIR/output" > "$TMP_OUTPUT_FILE"
|
108
|
+
cat "$TMP_OUTPUT_FILE"
|
109
|
+
}
|
110
|
+
|
111
|
+
# prepare the callback payload
|
112
|
+
payload() {
|
113
|
+
if [ -n "$1" ]; then
|
114
|
+
exit_code="$1"
|
115
|
+
else
|
116
|
+
exit_code=null
|
117
|
+
fi
|
118
|
+
|
119
|
+
if [ -e "$BASE_DIR/manual_mode" ]; then
|
120
|
+
manual_mode=true
|
121
|
+
output=$(prepare_output | base64 -w0)
|
122
|
+
else
|
123
|
+
manual_mode=false
|
124
|
+
fi
|
125
|
+
|
126
|
+
echo "{ \"exit_code\": $exit_code,"\
|
127
|
+
" \"step_id\": \"$STEP_ID\","\
|
128
|
+
" \"manual_mode\": $manual_mode,"\
|
129
|
+
" \"output\": \"$output\" }"
|
130
|
+
}
|
131
|
+
|
132
|
+
if [ "$1" = "push_update" ]; then
|
133
|
+
if [ -e "$BASE_DIR/exit_code" ]; then
|
134
|
+
exit_code="$(cat "$BASE_DIR/exit_code")"
|
135
|
+
action="done"
|
136
|
+
else
|
137
|
+
exit_code=""
|
138
|
+
action="update"
|
139
|
+
fi
|
140
|
+
$CURL -X POST -d "$(payload $exit_code)" -u "$AUTH" "$URL_PREFIX"/$action 2>>"$BASE_DIR/curl_stderr"
|
141
|
+
success=$?
|
142
|
+
else
|
143
|
+
prepare_output
|
144
|
+
success=$?
|
145
|
+
fi
|
146
|
+
|
147
|
+
if [ "$success" = 0 ] && [ -e "$TMP_OUTPUT_FILE" ]; then
|
148
|
+
# in case the retrieval was successful, move the position of the cursor to be read next time
|
149
|
+
bytes=$(wc --bytes < "$TMP_OUTPUT_FILE")
|
150
|
+
expr "${position}" + "${bytes}" > "$BASE_DIR/position"
|
151
|
+
fi
|