dk 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/dk/task.rb
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'much-plugin'
|
2
|
+
require 'dk'
|
3
|
+
require 'dk/remote'
|
4
|
+
require 'dk/runner'
|
5
|
+
|
6
|
+
module Dk
|
7
|
+
|
8
|
+
module Task
|
9
|
+
include MuchPlugin
|
10
|
+
|
11
|
+
class ParamsHash < Hash
|
12
|
+
def try_param(key)
|
13
|
+
begin; self.[](key); rescue Dk::NoParamError; nil; end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
plugin_included do
|
18
|
+
include InstanceMethods
|
19
|
+
extend ClassMethods
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
|
25
|
+
def initialize(runner, params = nil)
|
26
|
+
params ||= {}
|
27
|
+
@dk_runner = runner
|
28
|
+
@dk_params = ParamsHash.new{ |h, k| @dk_runner.params[k] }
|
29
|
+
@dk_params.merge!(params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def dk_run
|
33
|
+
self.dk_run_callbacks 'before'
|
34
|
+
@dk_runner.log_task_run(self.class) do
|
35
|
+
catch(:halt){ self.run! }
|
36
|
+
end
|
37
|
+
self.dk_run_callbacks 'after'
|
38
|
+
end
|
39
|
+
|
40
|
+
def run!
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def dk_run_callbacks(named)
|
45
|
+
callbacks = CallbackSet.new
|
46
|
+
callbacks.add_callbacks(@dk_runner.task_callbacks("prepend_#{named}", self.class))
|
47
|
+
callbacks.add_callbacks(self.class.send("#{named}_callbacks") || [])
|
48
|
+
callbacks.add_callbacks(@dk_runner.task_callbacks(named, self.class))
|
49
|
+
callbacks.each{ |c| run_task(c.task_class, c.params) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def dk_dsl_ssh_hosts
|
53
|
+
@dk_dsl_ssh_hosts ||= self.instance_eval(&self.class.ssh_hosts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(other_task)
|
57
|
+
self.class == other_task.class
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Helpers
|
63
|
+
|
64
|
+
def run_task(task_class, params = nil)
|
65
|
+
@dk_runner.run_task(task_class, params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def cmd(cmd_str, *args)
|
69
|
+
given_opts = args.last.kind_of?(::Hash) ? args.pop : nil
|
70
|
+
input = args.last
|
71
|
+
@dk_runner.cmd(self, cmd_str, input, given_opts)
|
72
|
+
end
|
73
|
+
|
74
|
+
def cmd!(cmd_str, *args)
|
75
|
+
cmd = cmd(cmd_str, *args)
|
76
|
+
if !cmd.success?
|
77
|
+
raise CmdRunError, "error running `#{cmd.cmd_str}`", caller
|
78
|
+
end
|
79
|
+
cmd
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssh(cmd_str, *args)
|
83
|
+
given_opts = args.last.kind_of?(::Hash) ? args.pop : nil
|
84
|
+
input = args.last
|
85
|
+
@dk_runner.ssh(self, cmd_str, input, given_opts, dk_build_ssh_opts(given_opts))
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssh!(cmd_str, *args)
|
89
|
+
cmd = ssh(cmd_str, *args)
|
90
|
+
if !cmd.success?
|
91
|
+
raise SSHRunError, "error running `#{cmd.cmd_str}` over ssh", caller
|
92
|
+
end
|
93
|
+
cmd
|
94
|
+
end
|
95
|
+
|
96
|
+
def params
|
97
|
+
@dk_params
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_param(key, value)
|
101
|
+
@dk_runner.set_param(key, value)
|
102
|
+
end
|
103
|
+
|
104
|
+
def param?(key)
|
105
|
+
@dk_params.key?(key) || @dk_runner.params.key?(key)
|
106
|
+
end
|
107
|
+
|
108
|
+
def try_param(key)
|
109
|
+
@dk_params.try_param(key)
|
110
|
+
end
|
111
|
+
|
112
|
+
def before(subject, callback, params = nil)
|
113
|
+
@dk_runner.add_task_callback('before', subject, callback, params)
|
114
|
+
end
|
115
|
+
|
116
|
+
def prepend_before(subject, callback, params = nil)
|
117
|
+
@dk_runner.add_task_callback('prepend_before', subject, callback, params)
|
118
|
+
end
|
119
|
+
|
120
|
+
def after(subject, callback, params = nil)
|
121
|
+
@dk_runner.add_task_callback('after', subject, callback, params)
|
122
|
+
end
|
123
|
+
|
124
|
+
def prepend_after(subject, callback, params = nil)
|
125
|
+
@dk_runner.add_task_callback('prepend_after', subject, callback, params)
|
126
|
+
end
|
127
|
+
|
128
|
+
def ssh_hosts(group_name = nil, *values)
|
129
|
+
@dk_runner.ssh_hosts(group_name, *values)
|
130
|
+
end
|
131
|
+
|
132
|
+
def ssh_cmd_str(cmd_str, opts = nil)
|
133
|
+
opts ||= {}
|
134
|
+
Remote.ssh_cmd_str(
|
135
|
+
cmd_str,
|
136
|
+
opts[:host].to_s,
|
137
|
+
dk_lookup_ssh_args(opts[:ssh_args]),
|
138
|
+
dk_lookup_host_ssh_args(opts[:host_ssh_args])
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def halt
|
143
|
+
throw :halt
|
144
|
+
end
|
145
|
+
|
146
|
+
def log_info(msg, *styles); @dk_runner.log_info(msg, *styles); end
|
147
|
+
def log_debug(msg, *styles); @dk_runner.log_debug(msg, *styles); end
|
148
|
+
def log_error(msg, *styles); @dk_runner.log_error(msg, *styles); end
|
149
|
+
|
150
|
+
def dk_build_ssh_opts(opts)
|
151
|
+
opts ||= {}
|
152
|
+
opts.merge({
|
153
|
+
:hosts => dk_lookup_ssh_hosts(opts[:hosts]),
|
154
|
+
:ssh_args => dk_lookup_ssh_args(opts[:ssh_args]),
|
155
|
+
:host_ssh_args => dk_lookup_host_ssh_args(opts[:host_ssh_args])
|
156
|
+
})
|
157
|
+
end
|
158
|
+
|
159
|
+
def dk_lookup_ssh_hosts(opts_hosts)
|
160
|
+
[*(
|
161
|
+
ssh_hosts[opts_hosts] ||
|
162
|
+
opts_hosts ||
|
163
|
+
ssh_hosts[self.dk_dsl_ssh_hosts] ||
|
164
|
+
self.dk_dsl_ssh_hosts
|
165
|
+
)]
|
166
|
+
end
|
167
|
+
|
168
|
+
def dk_lookup_ssh_args(opts_args)
|
169
|
+
opts_args || @dk_runner.ssh_args
|
170
|
+
end
|
171
|
+
|
172
|
+
def dk_lookup_host_ssh_args(opts_args)
|
173
|
+
opts_args || @dk_runner.host_ssh_args
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
CmdRunError = Class.new(RuntimeError)
|
179
|
+
SSHRunError = Class.new(RuntimeError)
|
180
|
+
|
181
|
+
module ClassMethods
|
182
|
+
|
183
|
+
def description(value = nil)
|
184
|
+
@description = value.to_s if !value.nil?
|
185
|
+
@description
|
186
|
+
end
|
187
|
+
alias_method :desc, :description
|
188
|
+
|
189
|
+
def before_callbacks; @before_callbacks ||= CallbackSet.new; end
|
190
|
+
def after_callbacks; @after_callbacks ||= CallbackSet.new; end
|
191
|
+
|
192
|
+
def before_callback_task_classes; self.before_callbacks.map(&:task_class); end
|
193
|
+
def after_callback_task_classes; self.after_callbacks.map(&:task_class); end
|
194
|
+
|
195
|
+
def before(task_class, params = nil)
|
196
|
+
self.before_callbacks << Callback.new(task_class, params)
|
197
|
+
end
|
198
|
+
|
199
|
+
def after(task_class, params = nil)
|
200
|
+
self.after_callbacks << Callback.new(task_class, params)
|
201
|
+
end
|
202
|
+
|
203
|
+
def prepend_before(task_class, params = nil)
|
204
|
+
self.before_callbacks.unshift(Callback.new(task_class, params))
|
205
|
+
end
|
206
|
+
|
207
|
+
def prepend_after(task_class, params = nil)
|
208
|
+
self.after_callbacks.unshift(Callback.new(task_class, params))
|
209
|
+
end
|
210
|
+
|
211
|
+
def ssh_hosts(value = nil, &block)
|
212
|
+
@ssh_hosts = block || proc{ value } if !block.nil? || !value.nil?
|
213
|
+
@ssh_hosts || proc{}
|
214
|
+
end
|
215
|
+
|
216
|
+
def run_only_once(value = nil)
|
217
|
+
@run_only_once = !!value if !value.nil?
|
218
|
+
@run_only_once
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
Callback = Struct.new(:task_class, :params)
|
224
|
+
|
225
|
+
class CallbackSet < ::Array
|
226
|
+
|
227
|
+
def <<(callback); super unless self.include?(callback); end
|
228
|
+
def unshift(callback); super unless self.include?(callback); end
|
229
|
+
|
230
|
+
def add_callbacks(callbacks)
|
231
|
+
callbacks.each{ |cb| self.<<(cb) }
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
module TestHelpers
|
237
|
+
include MuchPlugin
|
238
|
+
|
239
|
+
plugin_included do
|
240
|
+
require 'dk/test_runner'
|
241
|
+
include InstanceMethods
|
242
|
+
end
|
243
|
+
|
244
|
+
module InstanceMethods
|
245
|
+
|
246
|
+
def test_runner(task_class, args = nil)
|
247
|
+
Dk::TestRunner.new(args).tap do |runner|
|
248
|
+
runner.task_class = task_class
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_task(task_class, args = nil)
|
253
|
+
test_runner(task_class, args).task
|
254
|
+
end
|
255
|
+
|
256
|
+
def ssh_cmd_str(task, *args)
|
257
|
+
task.instance_eval{ ssh_cmd_str(*args) }
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
data/lib/dk/task_run.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'dk/has_the_runs'
|
2
|
+
require 'dk/has_the_stubs'
|
3
|
+
require 'dk/local'
|
4
|
+
require 'dk/remote'
|
5
|
+
require 'dk/runner'
|
6
|
+
require 'dk/task_run'
|
7
|
+
|
8
|
+
module Dk
|
9
|
+
|
10
|
+
class TestRunner < Runner
|
11
|
+
include HasTheRuns
|
12
|
+
include HasTheStubs
|
13
|
+
|
14
|
+
attr_accessor :task_class
|
15
|
+
|
16
|
+
# test runners are designed to only run their task
|
17
|
+
def run(params = nil)
|
18
|
+
self.task(params).tap(&:dk_run)
|
19
|
+
end
|
20
|
+
|
21
|
+
# don't run any sub-tasks, just track that a sub-task was run
|
22
|
+
def run_task(task_class, params = nil)
|
23
|
+
TaskRun.new(task_class, params).tap{ |tr| self.runs << tr }
|
24
|
+
end
|
25
|
+
|
26
|
+
# track that a local cmd was run
|
27
|
+
def cmd(task, cmd_str, input, given_opts)
|
28
|
+
super(task, cmd_str, input, given_opts).tap{ |c| self.runs << c }
|
29
|
+
end
|
30
|
+
|
31
|
+
# track that a remote cmd was run
|
32
|
+
def ssh(task, cmd_str, input, given_opts, ssh_opts)
|
33
|
+
super(task, cmd_str, input, given_opts, ssh_opts).tap{ |c| self.runs << c }
|
34
|
+
end
|
35
|
+
|
36
|
+
# test task API
|
37
|
+
|
38
|
+
def task(params = nil)
|
39
|
+
@task ||= build_task(self.task_class, params)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def has_the_stubs_build_local_cmd(cmd_str, given_opts)
|
45
|
+
Local::CmdSpy.new(cmd_str, given_opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_the_stubs_build_remote_cmd(cmd_str, ssh_opts)
|
49
|
+
Remote::CmdSpy.new(cmd_str, ssh_opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'dk/dry_runner'
|
2
|
+
require 'dk/has_the_runs'
|
3
|
+
require 'dk/null_logger'
|
4
|
+
require 'dk/task_run'
|
5
|
+
|
6
|
+
module Dk
|
7
|
+
|
8
|
+
class TreeRunner < DryRunner
|
9
|
+
include HasTheRuns
|
10
|
+
|
11
|
+
LEVEL_PREFIX = ' '.freeze
|
12
|
+
LEVEL_BULLET = '|-- '.freeze
|
13
|
+
|
14
|
+
def initialize(config, kernel)
|
15
|
+
super(config, :logger => NullLogger.new) # disable any logging
|
16
|
+
|
17
|
+
@task_run_stack = [self]
|
18
|
+
@run_num = 0
|
19
|
+
@kernel = kernel
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(*args)
|
23
|
+
# wipe the task runs before every run; that way `output_task_runs` outputs
|
24
|
+
# just this run's task runs
|
25
|
+
self.runs.clear
|
26
|
+
|
27
|
+
# increment the run num and run the task
|
28
|
+
@run_num += 1
|
29
|
+
task = super
|
30
|
+
|
31
|
+
# recursively output the task runs in a tree format
|
32
|
+
output_task_runs(self.runs, 0, "#{@run_num}) ".rjust(LEVEL_PREFIX.size, ' '))
|
33
|
+
|
34
|
+
# return the top-level task that was run
|
35
|
+
task
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# track all task runs
|
41
|
+
def build_and_run_task(task_class, params = nil)
|
42
|
+
task_run = TaskRun.new(task_class, params)
|
43
|
+
@task_run_stack.last.runs << task_run
|
44
|
+
|
45
|
+
@task_run_stack.push(task_run)
|
46
|
+
task = super(task_class, params)
|
47
|
+
@task_run_stack.pop
|
48
|
+
task
|
49
|
+
end
|
50
|
+
|
51
|
+
def output_task_runs(runs, level, prefix = nil)
|
52
|
+
runs.each do |task_run|
|
53
|
+
# recursively output the prefix and task class on indented, bulleted lines
|
54
|
+
@kernel.puts "#{LEVEL_PREFIX*level}" \
|
55
|
+
"#{LEVEL_BULLET if level > 0}" \
|
56
|
+
"#{prefix}#{task_run.task_class}"
|
57
|
+
output_task_runs(task_run.runs, level+1)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/dk/version.rb
CHANGED
data/lib/dk.rb
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
-
require
|
1
|
+
require 'dk/version'
|
2
|
+
require 'dk/config'
|
3
|
+
require 'dk/task'
|
2
4
|
|
3
5
|
module Dk
|
6
|
+
|
7
|
+
def self.config
|
8
|
+
@config ||= Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configure(&block)
|
12
|
+
self.config.init_procs << block
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.init
|
16
|
+
self.config.init
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reset
|
20
|
+
@config = Config.new
|
21
|
+
end
|
22
|
+
|
23
|
+
NoticeError = Class.new(RuntimeError)
|
24
|
+
NoParamError = Class.new(ArgumentError)
|
25
|
+
|
4
26
|
end
|
data/test/helper.rb
CHANGED
@@ -2,13 +2,18 @@
|
|
2
2
|
# put any test helpers here
|
3
3
|
|
4
4
|
# add the root dir to the load path
|
5
|
-
|
5
|
+
require 'pathname'
|
6
|
+
ROOT_PATH = Pathname.new(File.expand_path("../..", __FILE__))
|
7
|
+
$LOAD_PATH.unshift(ROOT_PATH)
|
6
8
|
|
7
9
|
# require pry for debugging (`binding.pry`)
|
8
10
|
require 'pry'
|
9
11
|
|
10
12
|
require 'test/support/factory'
|
11
13
|
|
14
|
+
# put scmd in test mode
|
15
|
+
ENV['SCMD_TEST_MODE'] = '1'
|
16
|
+
|
12
17
|
# 1.8.7 backfills
|
13
18
|
|
14
19
|
# Array#sample
|
data/test/support/factory.rb
CHANGED
@@ -3,4 +3,42 @@ require 'assert/factory'
|
|
3
3
|
module Factory
|
4
4
|
extend Assert::Factory
|
5
5
|
|
6
|
+
def self.ssh_cmd_opts
|
7
|
+
cmd_opts = { Factory.string => Factory.string }
|
8
|
+
# optionally have these options, this will ensure the default ssh cmd opts
|
9
|
+
# are used if these aren't provided
|
10
|
+
if Factory.boolean
|
11
|
+
cmd_opts.merge!({
|
12
|
+
:hosts => Factory.hosts,
|
13
|
+
:ssh_args => Factory.string,
|
14
|
+
:host_ssh_args => { Factory.string => Factory.string }
|
15
|
+
})
|
16
|
+
end
|
17
|
+
cmd_opts
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.stdout
|
21
|
+
Factory.integer(3).times.map{ Factory.string }.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.stderr
|
25
|
+
Factory.integer(3).times.map{ Factory.string }.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.exitstatus
|
29
|
+
[0, 1].sample
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.hosts
|
33
|
+
Factory.integer(3).times.map{ "#{Factory.string}.example.com" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.log_file
|
37
|
+
ROOT_PATH.join("test/support/log/#{Factory.string}.txt")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.task_callback(task_class, params = nil)
|
41
|
+
Dk::Task::Callback.new(task_class, params || {})
|
42
|
+
end
|
43
|
+
|
6
44
|
end
|
File without changes
|