dk 0.0.1 → 0.1.0
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 +5 -5
- data/README.md +643 -1
- data/bin/dk +7 -0
- data/dk.gemspec +7 -3
- data/lib/dk/ansi.rb +98 -0
- data/lib/dk/cli.rb +173 -0
- data/lib/dk/config.rb +217 -0
- data/lib/dk/config_runner.rb +24 -0
- data/lib/dk/dk_runner.rb +13 -0
- data/lib/dk/dry_runner.rb +43 -0
- data/lib/dk/has_set_param.rb +42 -0
- data/lib/dk/has_ssh_opts.rb +36 -0
- data/lib/dk/has_the_runs.rb +23 -0
- data/lib/dk/has_the_stubs.rb +116 -0
- data/lib/dk/local.rb +84 -0
- data/lib/dk/null_logger.rb +13 -0
- data/lib/dk/remote.rb +132 -0
- data/lib/dk/runner.rb +202 -0
- data/lib/dk/task.rb +266 -0
- data/lib/dk/task_run.rb +17 -0
- data/lib/dk/test_runner.rb +54 -0
- data/lib/dk/tree_runner.rb +64 -0
- data/lib/dk/version.rb +1 -1
- data/lib/dk.rb +23 -1
- data/test/helper.rb +6 -1
- data/test/support/config/dk.rb +7 -0
- data/test/support/config/task_defs.rb +10 -0
- data/test/support/factory.rb +38 -0
- data/test/support/log/.gitkeep +0 -0
- data/test/system/has_the_stubs_tests.rb +355 -0
- data/test/system/runner_tests.rb +222 -0
- data/test/unit/ansi_tests.rb +40 -0
- data/test/unit/cli_tests.rb +317 -0
- data/test/unit/config_runner_tests.rb +60 -0
- data/test/unit/config_tests.rb +427 -0
- data/test/unit/dk_runner_tests.rb +34 -0
- data/test/unit/dk_tests.rb +49 -0
- data/test/unit/dry_runner_tests.rb +71 -0
- data/test/unit/has_set_param_tests.rb +46 -0
- data/test/unit/has_ssh_opts_tests.rb +81 -0
- data/test/unit/has_the_runs_tests.rb +37 -0
- data/test/unit/has_the_stubs_tests.rb +279 -0
- data/test/unit/local_tests.rb +174 -0
- data/test/unit/null_logger_tests.rb +17 -0
- data/test/unit/remote_tests.rb +330 -0
- data/test/unit/runner_tests.rb +398 -0
- data/test/unit/task_run_tests.rb +40 -0
- data/test/unit/task_tests.rb +943 -0
- data/test/unit/test_runner_tests.rb +189 -0
- data/test/unit/tree_runner_tests.rb +152 -0
- metadata +106 -9
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'much-plugin'
|
2
|
+
require 'dk/local'
|
3
|
+
require 'dk/remote'
|
4
|
+
|
5
|
+
module Dk
|
6
|
+
|
7
|
+
module HasTheStubs
|
8
|
+
include MuchPlugin
|
9
|
+
|
10
|
+
plugin_included do
|
11
|
+
include InstanceMethods
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
|
17
|
+
# cmd stub api
|
18
|
+
|
19
|
+
def local_cmd_stubs
|
20
|
+
@local_cmd_stubs ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_cmd(cmd_str, args = nil, &block)
|
24
|
+
args ||= {}
|
25
|
+
|
26
|
+
cmd_str_proc = get_cmd_ssh_proc(cmd_str)
|
27
|
+
input_proc = get_cmd_ssh_proc(args[:input])
|
28
|
+
given_opts_proc = get_cmd_ssh_proc(args[:opts])
|
29
|
+
|
30
|
+
local_cmd_stubs.unshift(
|
31
|
+
Stub.new(cmd_str_proc, input_proc, given_opts_proc, block)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def unstub_all_cmds
|
36
|
+
local_cmd_stubs.clear
|
37
|
+
end
|
38
|
+
|
39
|
+
# ssh stub API
|
40
|
+
|
41
|
+
def remote_cmd_stubs
|
42
|
+
@remote_cmd_stubs ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
def stub_ssh(cmd_str, args = nil, &block)
|
46
|
+
args ||= {}
|
47
|
+
|
48
|
+
cmd_str_proc = get_cmd_ssh_proc(cmd_str)
|
49
|
+
input_proc = get_cmd_ssh_proc(args[:input])
|
50
|
+
given_opts_proc = get_cmd_ssh_proc(args[:opts])
|
51
|
+
|
52
|
+
remote_cmd_stubs.unshift(
|
53
|
+
Stub.new(cmd_str_proc, input_proc, given_opts_proc, block)
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def unstub_all_ssh
|
58
|
+
remote_cmd_stubs.clear
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def get_cmd_ssh_proc(obj)
|
64
|
+
obj.kind_of?(::Proc) ? obj : proc{ obj }
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_cmd_ssh_stub_block(stubs, task, cmd_str, input, given_opts)
|
68
|
+
stub = stubs.find do |stub|
|
69
|
+
task.instance_eval(&stub.cmd_str_proc) == cmd_str &&
|
70
|
+
task.instance_eval(&stub.input_proc) == input &&
|
71
|
+
task.instance_eval(&stub.given_opts_proc) == given_opts
|
72
|
+
end
|
73
|
+
stub ? stub.block : nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# if the cmd is stubbed, build a spy and apply the stub (or return the
|
77
|
+
# cached spy), otherwise let the runner decide how to handle the local
|
78
|
+
# cmd
|
79
|
+
def build_local_cmd(task, cmd_str, input, given_opts)
|
80
|
+
b = find_cmd_ssh_stub_block(local_cmd_stubs, task, cmd_str, input, given_opts)
|
81
|
+
if b
|
82
|
+
Local::CmdSpy.new(cmd_str, given_opts).tap(&b)
|
83
|
+
else
|
84
|
+
has_the_stubs_build_local_cmd(cmd_str, given_opts)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_the_stubs_build_local_cmd(cmd_str, given_opts)
|
89
|
+
raise NotImplementedError
|
90
|
+
end
|
91
|
+
|
92
|
+
# if the cmd is stubbed, build a spy and apply the stub (or return the
|
93
|
+
# cached spy), otherwise let the runner decide how to handle the remote
|
94
|
+
# cmd; when building the spy use the ssh opts, this allows stubbing and
|
95
|
+
# calling ssh cmds with the same opts but also allows building a valid
|
96
|
+
# remote cmd that has an ssh host
|
97
|
+
def build_remote_cmd(task, cmd_str, input, given_opts, ssh_opts)
|
98
|
+
b = find_cmd_ssh_stub_block(remote_cmd_stubs, task, cmd_str, input, given_opts)
|
99
|
+
if b
|
100
|
+
Remote::CmdSpy.new(cmd_str, ssh_opts).tap(&b)
|
101
|
+
else
|
102
|
+
has_the_stubs_build_remote_cmd(cmd_str, ssh_opts)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_the_stubs_build_remote_cmd(cmd_str, ssh_opts)
|
107
|
+
raise NotImplementedError
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
Stub = Struct.new(:cmd_str_proc, :input_proc, :given_opts_proc, :block)
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/lib/dk/local.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'scmd'
|
2
|
+
|
3
|
+
module Dk; end
|
4
|
+
module Dk::Local
|
5
|
+
|
6
|
+
class BaseCmd
|
7
|
+
|
8
|
+
attr_reader :scmd, :cmd_str
|
9
|
+
|
10
|
+
def initialize(scmd_or_spy_klass, cmd_str, opts)
|
11
|
+
opts ||= {}
|
12
|
+
|
13
|
+
@cmd_str = cmd_str
|
14
|
+
@scmd = scmd_or_spy_klass.new(@cmd_str, :env => opts[:env])
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s; self.cmd_str; end
|
18
|
+
|
19
|
+
def run(input = nil)
|
20
|
+
@scmd.run(input)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def stdout; @scmd.stdout; end
|
25
|
+
def stderr; @scmd.stderr; end
|
26
|
+
def success?; @scmd.success?; end
|
27
|
+
|
28
|
+
def output_lines
|
29
|
+
build_stdout_lines(self.stdout) + build_stderr_lines(self.stderr)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def build_stdout_lines(stdout)
|
35
|
+
build_output_lines('stdout', stdout)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_stderr_lines(stderr)
|
39
|
+
build_output_lines('stderr', stderr)
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_output_lines(name, output)
|
43
|
+
output.to_s.strip.split("\n").map{ |line| OutputLine.new(name, line) }
|
44
|
+
end
|
45
|
+
|
46
|
+
OutputLine = Struct.new(:name, :line)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class Cmd < BaseCmd
|
51
|
+
|
52
|
+
def initialize(cmd_str, opts = nil)
|
53
|
+
super(Scmd, cmd_str, opts)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class CmdSpy < BaseCmd
|
59
|
+
|
60
|
+
attr_reader :cmd_opts
|
61
|
+
|
62
|
+
def initialize(cmd_str, opts = nil)
|
63
|
+
require 'scmd/command_spy'
|
64
|
+
super(Scmd::CommandSpy, cmd_str, opts)
|
65
|
+
@cmd_opts = opts
|
66
|
+
end
|
67
|
+
|
68
|
+
def run_input
|
69
|
+
return nil unless self.run_called?
|
70
|
+
self.run_calls.first.input
|
71
|
+
end
|
72
|
+
|
73
|
+
def stdout=(value); @scmd.stdout = value; end
|
74
|
+
def stderr=(value); @scmd.stderr = value; end
|
75
|
+
def exitstatus=(value); @scmd.exitstatus = value; end
|
76
|
+
|
77
|
+
def run_calls; @scmd.run_calls; end
|
78
|
+
def run_called?; @scmd.run_called?; end
|
79
|
+
|
80
|
+
def ssh?; false; end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/dk/remote.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'dk/config'
|
2
|
+
require 'dk/local'
|
3
|
+
|
4
|
+
module Dk; end
|
5
|
+
module Dk::Remote
|
6
|
+
|
7
|
+
def self.ssh_cmd_str(cmd_str, host, args, host_args)
|
8
|
+
host_args = host_args[host.to_s] if !host.nil?
|
9
|
+
val = "\"#{cmd_str.gsub(/\s+/, ' ')}\"".gsub("\\", "\\\\\\").gsub('"', '\"')
|
10
|
+
"ssh #{args} #{host_args} #{host} -- \"sh -c #{val}\""
|
11
|
+
end
|
12
|
+
|
13
|
+
class BaseCmd
|
14
|
+
|
15
|
+
attr_reader :hosts, :ssh_args, :host_ssh_args, :cmd_str, :local_cmds
|
16
|
+
|
17
|
+
def initialize(local_cmd_or_spy_klass, cmd_str, opts)
|
18
|
+
opts ||= {}
|
19
|
+
if nil_or_empty_or_missing_hosts(opts[:hosts])
|
20
|
+
raise NoHostsError, "no hosts to run cmd on (#{opts[:hosts].inspect})"
|
21
|
+
end
|
22
|
+
|
23
|
+
@hosts = opts[:hosts].sort
|
24
|
+
@ssh_args = opts[:ssh_args] || Dk::Config::DEFAULT_SSH_ARGS.dup
|
25
|
+
@host_ssh_args = opts[:host_ssh_args] || Dk::Config::DEFAULT_HOST_SSH_ARGS.dup
|
26
|
+
@cmd_str = cmd_str
|
27
|
+
|
28
|
+
@local_cmds = @hosts.inject({}) do |cmds, host|
|
29
|
+
cmds[host] = local_cmd_or_spy_klass.new(self.ssh_cmd_str(host), {
|
30
|
+
:env => opts[:env],
|
31
|
+
:dry_tree_run => opts[:dry_tree_run]
|
32
|
+
})
|
33
|
+
cmds
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s; self.cmd_str; end
|
38
|
+
|
39
|
+
def ssh_cmd_str(host)
|
40
|
+
build_ssh_cmd_str(@cmd_str, host, @ssh_args, @host_ssh_args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def run(input = nil)
|
44
|
+
self.hosts.each{ |host| @local_cmds[host].scmd.start(input) }
|
45
|
+
self.hosts.each{ |host| @local_cmds[host].scmd.wait }
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def stdout
|
50
|
+
self.hosts.inject('') do |out, host|
|
51
|
+
out.empty? ? @local_cmds[host].stdout.to_s : out
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def stderr
|
56
|
+
self.hosts.inject('') do |err, host|
|
57
|
+
err.empty? ? @local_cmds[host].stderr.to_s : err
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def success?
|
62
|
+
self.hosts.inject(true) do |success, host|
|
63
|
+
success && @local_cmds[host].success?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def output_lines
|
68
|
+
self.hosts.inject([]) do |lines, host|
|
69
|
+
lines + build_output_lines(host, @local_cmds[host].output_lines)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def nil_or_empty_or_missing_hosts(h)
|
76
|
+
h.nil? ||
|
77
|
+
!h.respond_to?(:empty?) || h.empty? ||
|
78
|
+
!h.respond_to?(:select) || h.select(&:nil?).size > 0
|
79
|
+
end
|
80
|
+
|
81
|
+
# escape everything properly; run in sh to ensure full profile is loaded
|
82
|
+
def build_ssh_cmd_str(cmd_str, host, args, host_args)
|
83
|
+
Dk::Remote.ssh_cmd_str(cmd_str, host, args, host_args)
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_output_lines(host, local_cmd_output_lines)
|
87
|
+
local_cmd_output_lines.map{ |ol| OutputLine.new(host, ol.name, ol.line) }
|
88
|
+
end
|
89
|
+
|
90
|
+
OutputLine = Struct.new(:host, :name, :line)
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
NoHostsError = Class.new(ArgumentError)
|
95
|
+
|
96
|
+
class Cmd < BaseCmd
|
97
|
+
|
98
|
+
def initialize(cmd_str, opts = nil)
|
99
|
+
super(Dk::Local::Cmd, cmd_str, opts)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class CmdSpy < BaseCmd
|
105
|
+
|
106
|
+
attr_reader :cmd_opts
|
107
|
+
|
108
|
+
def initialize(cmd_str, opts = nil)
|
109
|
+
super(Dk::Local::CmdSpy, cmd_str, opts)
|
110
|
+
@cmd_opts = opts
|
111
|
+
@first_local_cmd_spy = @local_cmds[@hosts.first]
|
112
|
+
end
|
113
|
+
|
114
|
+
def run_input
|
115
|
+
return nil unless self.run_called?
|
116
|
+
self.run_calls.first.input
|
117
|
+
end
|
118
|
+
|
119
|
+
# just set the first local cmd, this will have an overall effect
|
120
|
+
def stdout=(value); @first_local_cmd_spy.stdout = value; end
|
121
|
+
def stderr=(value); @first_local_cmd_spy.stderr = value; end
|
122
|
+
def exitstatus=(value); @first_local_cmd_spy.exitstatus = value; end
|
123
|
+
|
124
|
+
# just query the firs tlocal cmd - if run for one it was run for all
|
125
|
+
def run_calls; @first_local_cmd_spy.scmd.start_calls; end
|
126
|
+
def run_called?; @first_local_cmd_spy.scmd.start_called?; end
|
127
|
+
|
128
|
+
def ssh?; true; end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
data/lib/dk/runner.rb
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'set'
|
3
|
+
require 'dk'
|
4
|
+
require 'dk/ansi'
|
5
|
+
require 'dk/config'
|
6
|
+
require 'dk/has_set_param'
|
7
|
+
require 'dk/has_ssh_opts'
|
8
|
+
require 'dk/local'
|
9
|
+
require 'dk/null_logger'
|
10
|
+
require 'dk/remote'
|
11
|
+
|
12
|
+
module Dk
|
13
|
+
|
14
|
+
class Runner
|
15
|
+
include Dk::HasSetParam
|
16
|
+
include Dk::HasSSHOpts
|
17
|
+
|
18
|
+
TASK_START_LOG_PREFIX = ' >>> '.freeze
|
19
|
+
TASK_END_LOG_PREFIX = ' <<< '.freeze
|
20
|
+
INDENT_LOG_PREFIX = ' '.freeze
|
21
|
+
CMD_LOG_PREFIX = '[CMD] '.freeze
|
22
|
+
SSH_LOG_PREFIX = '[SSH] '.freeze
|
23
|
+
CMD_SSH_OUT_LOG_PREFIX = "> ".freeze
|
24
|
+
|
25
|
+
attr_reader :params, :logger
|
26
|
+
|
27
|
+
def initialize(opts = nil)
|
28
|
+
opts ||= {}
|
29
|
+
@params = Hash.new{ |h, k| raise Dk::NoParamError, "no param named `#{k}`" }
|
30
|
+
@params.merge!(dk_normalize_params(opts[:params]))
|
31
|
+
|
32
|
+
d = Config::DEFAULT_CALLBACKS
|
33
|
+
@task_callbacks = {
|
34
|
+
'before' => opts[:before_callbacks] || d.dup,
|
35
|
+
'prepend_before' => opts[:prepend_before_callbacks] || d.dup,
|
36
|
+
'after' => opts[:after_callbacks] || d.dup,
|
37
|
+
'prepend_after' => opts[:prepend_after_callbacks] || d.dup
|
38
|
+
}
|
39
|
+
|
40
|
+
@ssh_hosts = opts[:ssh_hosts] || Config::DEFAULT_SSH_HOSTS.dup
|
41
|
+
@ssh_args = opts[:ssh_args] || Config::DEFAULT_SSH_ARGS.dup
|
42
|
+
@host_ssh_args = opts[:host_ssh_args] || Config::DEFAULT_HOST_SSH_ARGS.dup
|
43
|
+
|
44
|
+
@logger = opts[:logger] || NullLogger.new
|
45
|
+
|
46
|
+
@has_run_task_classes = Set.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def task_callbacks(named, task_class)
|
50
|
+
@task_callbacks[named][task_class] || []
|
51
|
+
end
|
52
|
+
|
53
|
+
def task_callback_task_classes(named, task_class)
|
54
|
+
task_callbacks(named, task_class).map(&:task_class)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_task_callback(named, subject_task_class, callback_task_class, params)
|
58
|
+
@task_callbacks[named][subject_task_class] << Task::Callback.new(
|
59
|
+
callback_task_class,
|
60
|
+
params
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
# called by CLI on top-level tasks
|
65
|
+
def run(task_class, params = nil)
|
66
|
+
check_run_once_and_build_and_run_task(task_class, params)
|
67
|
+
end
|
68
|
+
|
69
|
+
# called by other tasks on sub-tasks
|
70
|
+
def run_task(task_class, params = nil)
|
71
|
+
check_run_once_and_build_and_run_task(task_class, params)
|
72
|
+
end
|
73
|
+
|
74
|
+
def log_info(msg, *ansi_styles)
|
75
|
+
self.logger.info("#{INDENT_LOG_PREFIX}#{Ansi.styled_msg(msg, *ansi_styles)}")
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_debug(msg, *ansi_styles)
|
79
|
+
self.logger.debug("#{INDENT_LOG_PREFIX}#{Ansi.styled_msg(msg, *ansi_styles)}")
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_error(msg, *ansi_styles)
|
83
|
+
self.logger.error("#{INDENT_LOG_PREFIX}#{Ansi.styled_msg(msg, *ansi_styles)}")
|
84
|
+
end
|
85
|
+
|
86
|
+
def log_task_run(task_class, &run_block)
|
87
|
+
self.logger.debug "#{TASK_START_LOG_PREFIX}#{task_class}"
|
88
|
+
time = Benchmark.realtime(&run_block)
|
89
|
+
self.logger.debug "#{TASK_END_LOG_PREFIX}#{task_class} (#{self.pretty_run_time(time)})"
|
90
|
+
end
|
91
|
+
|
92
|
+
def log_cli_task_run(task_name, &run_block)
|
93
|
+
self.logger.info "Starting `#{task_name}`."
|
94
|
+
time = Benchmark.realtime(&run_block)
|
95
|
+
self.logger.info "`#{task_name}` finished in #{self.pretty_run_time(time)}."
|
96
|
+
self.logger.info ""
|
97
|
+
self.logger.info ""
|
98
|
+
end
|
99
|
+
|
100
|
+
def log_cli_run(cli_argv, &run_block)
|
101
|
+
15.times{ self.logger.debug "" }
|
102
|
+
self.logger.debug "===================================="
|
103
|
+
self.logger.debug ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> `#{cli_argv}`"
|
104
|
+
self.logger.debug "===================================="
|
105
|
+
time = Benchmark.realtime(&run_block)
|
106
|
+
self.logger.info "(#{self.pretty_run_time(time)})"
|
107
|
+
self.logger.debug "===================================="
|
108
|
+
self.logger.debug "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< `#{cli_argv}`"
|
109
|
+
self.logger.debug "===================================="
|
110
|
+
end
|
111
|
+
|
112
|
+
def cmd(task, cmd_str, input, given_opts)
|
113
|
+
build_and_run_local_cmd(task, cmd_str, input, given_opts)
|
114
|
+
end
|
115
|
+
|
116
|
+
def ssh(task, cmd_str, input, given_opts, ssh_opts)
|
117
|
+
build_and_run_remote_cmd(task, cmd_str, input, given_opts, ssh_opts)
|
118
|
+
end
|
119
|
+
|
120
|
+
def has_run_task?(task_class)
|
121
|
+
@has_run_task_classes.include?(task_class)
|
122
|
+
end
|
123
|
+
|
124
|
+
def pretty_run_time(raw_run_time)
|
125
|
+
if raw_run_time > 1.5 # seconds
|
126
|
+
"#{raw_run_time.to_i / 60}:#{(raw_run_time.round % 60).to_i.to_s.rjust(2, '0')}s"
|
127
|
+
else
|
128
|
+
"#{(raw_run_time * 1000 * 10.0).round / 10.0}ms"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def check_run_once_and_build_and_run_task(task_class, params = nil)
|
135
|
+
if task_class.run_only_once && self.has_run_task?(task_class)
|
136
|
+
build_task(task_class, params)
|
137
|
+
else
|
138
|
+
build_and_run_task(task_class, params)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_and_run_task(task_class, params = nil)
|
143
|
+
build_task(task_class, params).tap do |task|
|
144
|
+
task.dk_run
|
145
|
+
@has_run_task_classes << task_class
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def build_task(task_class, params = nil)
|
150
|
+
task_class.new(self, params)
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_and_run_local_cmd(task, cmd_str, input, given_opts)
|
154
|
+
local_cmd = build_local_cmd(task, cmd_str, input, given_opts)
|
155
|
+
log_local_cmd(local_cmd){ |cmd| cmd.run(input) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# input is needed for the `TestRunner` so it can use it with stubbing
|
159
|
+
# otherwise it is ignored when building a local cmd
|
160
|
+
def build_local_cmd(task, cmd_str, input, given_opts)
|
161
|
+
Local::Cmd.new(cmd_str, given_opts)
|
162
|
+
end
|
163
|
+
|
164
|
+
def log_local_cmd(cmd, &block)
|
165
|
+
self.logger.info("#{CMD_LOG_PREFIX}#{cmd.cmd_str}")
|
166
|
+
time = Benchmark.realtime{ block.call(cmd) }
|
167
|
+
self.logger.info("#{INDENT_LOG_PREFIX}(#{self.pretty_run_time(time)})")
|
168
|
+
cmd.output_lines.each do |output_line|
|
169
|
+
self.logger.debug("#{INDENT_LOG_PREFIX}#{CMD_SSH_OUT_LOG_PREFIX}#{output_line.line}")
|
170
|
+
end
|
171
|
+
cmd
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_and_run_remote_cmd(task, cmd_str, input, given_opts, ssh_opts)
|
175
|
+
remote_cmd = build_remote_cmd(task, cmd_str, input, given_opts, ssh_opts)
|
176
|
+
log_remote_cmd(remote_cmd){ |cmd| cmd.run(input) }
|
177
|
+
end
|
178
|
+
|
179
|
+
# input and given opts are needed for the `TestRunner` so it can use it with
|
180
|
+
# stubbing otherwise they are ignored when building a remote cmd
|
181
|
+
def build_remote_cmd(task, cmd_str, input, given_opts, ssh_opts)
|
182
|
+
Remote::Cmd.new(cmd_str, ssh_opts)
|
183
|
+
end
|
184
|
+
|
185
|
+
def log_remote_cmd(cmd, &block)
|
186
|
+
self.logger.info("#{SSH_LOG_PREFIX}#{cmd.cmd_str}")
|
187
|
+
self.logger.debug("#{INDENT_LOG_PREFIX}#{cmd.ssh_cmd_str('<host>')}")
|
188
|
+
cmd.hosts.each do |host|
|
189
|
+
self.logger.info("#{INDENT_LOG_PREFIX}[#{host}]")
|
190
|
+
end
|
191
|
+
time = Benchmark.realtime{ block.call(cmd) }
|
192
|
+
self.logger.info("#{INDENT_LOG_PREFIX}(#{self.pretty_run_time(time)})")
|
193
|
+
cmd.output_lines.each do |ol|
|
194
|
+
self.logger.debug "#{INDENT_LOG_PREFIX}[#{ol.host}] " \
|
195
|
+
"#{CMD_SSH_OUT_LOG_PREFIX}#{ol.line}"
|
196
|
+
end
|
197
|
+
cmd
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|