runbook 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/CHANGELOG.md +46 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +6 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +999 -0
  12. data/Rakefile +6 -0
  13. data/TODO.md +38 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +8 -0
  16. data/exe/runbook +5 -0
  17. data/images/runbook_anatomy_diagram.png +0 -0
  18. data/images/runbook_example.gif +0 -0
  19. data/images/runbook_execution_modes.png +0 -0
  20. data/lib/hacks/ssh_kit.rb +58 -0
  21. data/lib/runbook/cli.rb +90 -0
  22. data/lib/runbook/configuration.rb +110 -0
  23. data/lib/runbook/dsl.rb +21 -0
  24. data/lib/runbook/entities/book.rb +17 -0
  25. data/lib/runbook/entities/section.rb +7 -0
  26. data/lib/runbook/entities/step.rb +7 -0
  27. data/lib/runbook/entity.rb +127 -0
  28. data/lib/runbook/errors.rb +7 -0
  29. data/lib/runbook/extensions/add.rb +13 -0
  30. data/lib/runbook/extensions/description.rb +14 -0
  31. data/lib/runbook/extensions/sections.rb +15 -0
  32. data/lib/runbook/extensions/shared_variables.rb +51 -0
  33. data/lib/runbook/extensions/ssh_config.rb +76 -0
  34. data/lib/runbook/extensions/statements.rb +26 -0
  35. data/lib/runbook/extensions/steps.rb +14 -0
  36. data/lib/runbook/extensions/tmux.rb +13 -0
  37. data/lib/runbook/helpers/format_helper.rb +11 -0
  38. data/lib/runbook/helpers/ssh_kit_helper.rb +143 -0
  39. data/lib/runbook/helpers/tmux_helper.rb +174 -0
  40. data/lib/runbook/hooks.rb +88 -0
  41. data/lib/runbook/node.rb +23 -0
  42. data/lib/runbook/run.rb +283 -0
  43. data/lib/runbook/runner.rb +64 -0
  44. data/lib/runbook/runs/ssh_kit.rb +186 -0
  45. data/lib/runbook/statement.rb +22 -0
  46. data/lib/runbook/statements/ask.rb +11 -0
  47. data/lib/runbook/statements/assert.rb +25 -0
  48. data/lib/runbook/statements/capture.rb +14 -0
  49. data/lib/runbook/statements/capture_all.rb +14 -0
  50. data/lib/runbook/statements/command.rb +11 -0
  51. data/lib/runbook/statements/confirm.rb +10 -0
  52. data/lib/runbook/statements/description.rb +9 -0
  53. data/lib/runbook/statements/download.rb +12 -0
  54. data/lib/runbook/statements/layout.rb +10 -0
  55. data/lib/runbook/statements/note.rb +10 -0
  56. data/lib/runbook/statements/notice.rb +10 -0
  57. data/lib/runbook/statements/ruby_command.rb +9 -0
  58. data/lib/runbook/statements/tmux_command.rb +11 -0
  59. data/lib/runbook/statements/upload.rb +12 -0
  60. data/lib/runbook/statements/wait.rb +10 -0
  61. data/lib/runbook/toolbox.rb +43 -0
  62. data/lib/runbook/util/repo.rb +56 -0
  63. data/lib/runbook/util/runbook.rb +25 -0
  64. data/lib/runbook/util/sticky_hash.rb +26 -0
  65. data/lib/runbook/util/stored_pose.rb +54 -0
  66. data/lib/runbook/version.rb +3 -0
  67. data/lib/runbook/view.rb +24 -0
  68. data/lib/runbook/viewer.rb +24 -0
  69. data/lib/runbook/views/markdown.rb +109 -0
  70. data/lib/runbook.rb +110 -0
  71. data/runbook.gemspec +48 -0
  72. data/samples/hooks_runbook.rb +72 -0
  73. data/samples/layout_runbook.rb +26 -0
  74. data/samples/restart_nginx.rb +26 -0
  75. data/samples/simple_runbook.rb +41 -0
  76. metadata +324 -0
@@ -0,0 +1,283 @@
1
+ module Runbook
2
+ module Run
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ _register_kill_all_panes_hook(base)
6
+ _register_additional_step_whitespace_hook(base)
7
+ ::Runbook::Util::Repo.register_save_repo_hook(base)
8
+ ::Runbook::Util::Repo.register_delete_stored_repo_hook(base)
9
+ ::Runbook::Util::StoredPose.register_save_pose_hook(base)
10
+ ::Runbook::Util::StoredPose.register_delete_stored_pose_hook(base)
11
+ end
12
+
13
+ module ClassMethods
14
+ include Runbook::Hooks
15
+ include Runbook::Helpers::FormatHelper
16
+ include Runbook::Helpers::TmuxHelper
17
+
18
+ def execute(object, metadata)
19
+ return if should_skip?(metadata)
20
+
21
+ method = _method_name(object)
22
+ if respond_to?(method)
23
+ send(method, object, metadata)
24
+ else
25
+ msg = "ERROR! No execution rule for #{object.class} (#{_method_name(object)}) in #{to_s}"
26
+ metadata[:toolbox].error(msg)
27
+ return
28
+ end
29
+ end
30
+
31
+ def runbook__entities__book(object, metadata)
32
+ metadata[:toolbox].output("Executing #{object.title}...\n\n")
33
+ end
34
+
35
+ def runbook__entities__section(object, metadata)
36
+ metadata[:toolbox].output("Section #{metadata[:position]}: #{object.title}\n\n")
37
+ end
38
+
39
+ def runbook__entities__step(object, metadata)
40
+ toolbox = metadata[:toolbox]
41
+ title = " #{object.title}".rstrip
42
+ toolbox.output("Step #{metadata[:position]}:#{title}\n\n")
43
+ return if metadata[:auto] || metadata[:noop] ||
44
+ !metadata[:paranoid] || object.title.nil?
45
+ continue_result = toolbox.expand("Continue?", _step_choices)
46
+ _handle_continue_result(continue_result, object, metadata)
47
+ end
48
+
49
+ def runbook__statements__ask(object, metadata)
50
+ target = object.parent.dsl
51
+ existing_val = target.instance_variable_get(:"@#{object.into}")
52
+ default = existing_val || object.default
53
+
54
+ if metadata[:auto]
55
+ if default
56
+ target.singleton_class.class_eval { attr_accessor object.into }
57
+ target.send("#{object.into}=".to_sym, default)
58
+ return
59
+ end
60
+
61
+ error_msg = "ERROR! Can't execute ask statement without default in automatic mode!"
62
+ metadata[:toolbox].error(error_msg)
63
+ raise Runbook::Runner::ExecutionError, error_msg
64
+ end
65
+
66
+ if metadata[:noop]
67
+ default_msg = default ? " (default: #{default})" : ""
68
+ metadata[:toolbox].output("[NOOP] Ask: #{object.prompt} (store in: #{object.into})#{default_msg}")
69
+ return
70
+ end
71
+
72
+ result = metadata[:toolbox].ask(object.prompt, default: default)
73
+
74
+ target = object.parent.dsl
75
+ target.singleton_class.class_eval { attr_accessor object.into }
76
+ target.send("#{object.into}=".to_sym, result)
77
+ end
78
+
79
+ def runbook__statements__confirm(object, metadata)
80
+ if metadata[:auto]
81
+ metadata[:toolbox].output("Skipping confirmation (auto): #{object.prompt}")
82
+ else
83
+ if metadata[:noop]
84
+ metadata[:toolbox].output("[NOOP] Prompt: #{object.prompt}")
85
+ return
86
+ end
87
+
88
+ result = metadata[:toolbox].yes?(object.prompt)
89
+ metadata[:toolbox].exit(1) unless result
90
+ end
91
+ end
92
+
93
+ def runbook__statements__description(object, metadata)
94
+ metadata[:toolbox].output("Description:")
95
+ metadata[:toolbox].output("#{object.msg}\n")
96
+ end
97
+
98
+ def runbook__statements__layout(object, metadata)
99
+ if metadata[:noop]
100
+ metadata[:toolbox].output(
101
+ "[NOOP] Layout: #{object.structure.inspect}"
102
+ )
103
+ unless ENV["TMUX"]
104
+ msg = "Warning: layout statement called outside a tmux pane."
105
+ metadata[:toolbox].warn(msg)
106
+ end
107
+ return
108
+ end
109
+
110
+ unless ENV["TMUX"]
111
+ error_msg = "Error: layout statement called outside a tmux pane. Exiting..."
112
+ metadata[:toolbox].error(error_msg)
113
+ metadata[:toolbox].exit(1)
114
+ end
115
+
116
+ structure = object.structure
117
+ title = object.parent.title
118
+ layout_panes = setup_layout(structure, runbook_title: title)
119
+ metadata[:layout_panes].merge!(layout_panes)
120
+ end
121
+
122
+ def runbook__statements__note(object, metadata)
123
+ metadata[:toolbox].output("Note: #{object.msg}")
124
+ end
125
+
126
+ def runbook__statements__notice(object, metadata)
127
+ metadata[:toolbox].warn("Notice: #{object.msg}")
128
+ end
129
+
130
+ def runbook__statements__tmux_command(object, metadata)
131
+ if metadata[:noop]
132
+ metadata[:toolbox].output("[NOOP] Run: `#{object.cmd}` in pane #{object.pane}")
133
+ return
134
+ end
135
+
136
+ send_keys(object.cmd, metadata[:layout_panes][object.pane])
137
+ end
138
+
139
+ def runbook__statements__ruby_command(object, metadata)
140
+ if metadata[:noop]
141
+ metadata[:toolbox].output("[NOOP] Run the following Ruby block:\n")
142
+ begin
143
+ source = deindent(object.block.source)
144
+ metadata[:toolbox].output("```ruby\n#{source}\n```\n")
145
+ rescue ::MethodSource::SourceNotFoundError => e
146
+ metadata[:toolbox].output("Unable to retrieve source code")
147
+ end
148
+ return
149
+ end
150
+
151
+ next_index = metadata[:index] + 1
152
+ parent_items = object.parent.items
153
+ remaining_items = parent_items.slice!(next_index..-1)
154
+ object.parent.dsl.instance_exec(object, metadata, &object.block)
155
+ parent_items[next_index..-1].each { |item| item.dynamic! }
156
+ parent_items.push(*remaining_items)
157
+ end
158
+
159
+ def runbook__statements__wait(object, metadata)
160
+ if metadata[:noop]
161
+ metadata[:toolbox].output("[NOOP] Sleep #{object.time} seconds")
162
+ return
163
+ end
164
+
165
+ time = object.time
166
+ message = "Sleeping #{time} seconds [:bar] :current/:total"
167
+ pastel = Pastel.new
168
+ yellow = pastel.on_yellow(" ")
169
+ green = pastel.on_green(" ")
170
+ progress_bar = TTY::ProgressBar.new(
171
+ message,
172
+ total: time,
173
+ width: 60,
174
+ head: ">",
175
+ incomplete: yellow,
176
+ complete: green,
177
+ )
178
+ progress_bar.start
179
+ time.times do
180
+ sleep(1)
181
+ progress_bar.advance(1)
182
+ end
183
+ end
184
+
185
+ def should_skip?(metadata)
186
+ if metadata[:reversed] && metadata[:position].empty?
187
+ current_pose = "0"
188
+ else
189
+ current_pose = metadata[:position]
190
+ end
191
+ return false if current_pose.empty?
192
+ position = Gem::Version.new(current_pose)
193
+ start_at = Gem::Version.new(metadata[:start_at])
194
+ return position < start_at
195
+ end
196
+
197
+ def start_at_is_substep?(object, metadata)
198
+ return false unless object.is_a?(Entity)
199
+ return true if metadata[:position].empty?
200
+ metadata[:start_at].start_with?(metadata[:position])
201
+ end
202
+
203
+ def past_position?(current_position, position)
204
+ current_pose = Gem::Version.new(current_position)
205
+ pose = Gem::Version.new(position)
206
+ return pose <= current_pose
207
+ end
208
+
209
+ def _method_name(object)
210
+ object.class.to_s.underscore.gsub("/", "__")
211
+ end
212
+
213
+ def _step_choices
214
+ [
215
+ {key: "c", name: "Continue to execute this step", value: :continue},
216
+ {key: "s", name: "Skip this step", value: :skip},
217
+ {key: "j", name: "Jump to the specified position", value: :jump},
218
+ {key: "P", name: "Disable paranoid mode", value: :no_paranoid},
219
+ {key: "e", name: "Exit the runbook", value: :exit},
220
+ ]
221
+ end
222
+
223
+ def _handle_continue_result(result, object, metadata)
224
+ toolbox = metadata[:toolbox]
225
+ case result
226
+ when :continue
227
+ return
228
+ when :skip
229
+ position = metadata[:position]
230
+ current_step = position.split(".")[-1].to_i
231
+ new_step = current_step + 1
232
+ start_at = position.gsub(/\.#{current_step}$/, ".#{new_step}")
233
+ metadata[:start_at] = start_at
234
+ when :jump
235
+ result = toolbox.ask("What position would you like to jump to?")
236
+ if past_position?(metadata[:position], result)
237
+ metadata[:reverse] = true
238
+ metadata[:reversed] = true
239
+ end
240
+ metadata[:start_at] = result
241
+ when :no_paranoid
242
+ metadata[:paranoid] = false
243
+ when :exit
244
+ toolbox.exit(0)
245
+ end
246
+ end
247
+ end
248
+
249
+ def self._register_kill_all_panes_hook(base)
250
+ base.register_hook(
251
+ :kill_all_panes_after_book,
252
+ :after,
253
+ Runbook::Entities::Book,
254
+ ) do |object, metadata|
255
+ next if metadata[:noop] || metadata[:layout_panes].none?
256
+ if metadata[:auto]
257
+ metadata[:toolbox].output("Killing all opened tmux panes...")
258
+ kill_all_panes(metadata[:layout_panes])
259
+ else
260
+ prompt = "Kill all opened panes?"
261
+ result = metadata[:toolbox].yes?(prompt)
262
+ if result
263
+ kill_all_panes(metadata[:layout_panes])
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ def self._register_additional_step_whitespace_hook(base)
270
+ base.register_hook(
271
+ :add_additional_step_whitespace_hook,
272
+ :after,
273
+ Runbook::Statement,
274
+ ) do |object, metadata|
275
+ if object.parent.is_a?(Runbook::Entities::Step)
276
+ if object.parent.items.last == object
277
+ metadata[:toolbox].output("\n")
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,64 @@
1
+ module Runbook
2
+ class Runner
3
+ attr_reader :book
4
+
5
+ def initialize(book)
6
+ @book = book
7
+ end
8
+
9
+ def run(
10
+ run: :ssh_kit,
11
+ noop: false,
12
+ auto: false,
13
+ paranoid: true,
14
+ start_at: "0"
15
+ )
16
+ run = "Runbook::Runs::#{run.to_s.camelize}".constantize
17
+ toolbox = Runbook::Toolbox.new
18
+ metadata = Util::StickyHash.new.merge({
19
+ noop: noop,
20
+ auto: auto,
21
+ paranoid: Util::Glue.new(paranoid),
22
+ start_at: Util::Glue.new(start_at || "0"),
23
+ toolbox: Util::Glue.new(toolbox),
24
+ book_title: book.title,
25
+ }).
26
+ merge(Runbook::Entities::Book.initial_run_metadata).
27
+ merge(additional_metadata)
28
+
29
+ stored_pose = _stored_position(metadata)
30
+ if metadata[:start_at] == "0" && stored_pose
31
+ if _resume_previous_pose?(metadata, stored_pose)
32
+ metadata[:start_at] = stored_pose
33
+ end
34
+ end
35
+
36
+ if metadata[:start_at] != "0"
37
+ Util::Repo.load(metadata)
38
+ end
39
+
40
+ book.run(run, metadata)
41
+ end
42
+
43
+ def additional_metadata
44
+ {
45
+ layout_panes: {},
46
+ repo: {},
47
+ reverse: Util::Glue.new(false),
48
+ reversed: Util::Glue.new(false),
49
+ }
50
+ end
51
+
52
+ def _stored_position(metadata)
53
+ Runbook::Util::StoredPose.load(metadata)
54
+ end
55
+
56
+ def _resume_previous_pose?(metadata, pose)
57
+ return false if metadata[:auto] || metadata[:noop]
58
+ pose_msg = "Previous position detected: #{pose}"
59
+ metadata[:toolbox].output(pose_msg)
60
+ resume_msg = "Do you want to resume at this position?"
61
+ metadata[:toolbox].yes?(resume_msg)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,186 @@
1
+ module Runbook::Runs
2
+ module SSHKit
3
+ include Runbook::Run
4
+ extend Runbook::Helpers::SSHKitHelper
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def runbook__statements__assert(object, metadata)
12
+ cmd_ssh_config = find_ssh_config(object, :cmd_ssh_config)
13
+
14
+ if metadata[:noop]
15
+ ssh_config_output = render_ssh_config_output(cmd_ssh_config)
16
+ metadata[:toolbox].output(ssh_config_output) unless ssh_config_output.empty?
17
+ interval_msg = "(running every #{object.interval} second(s))"
18
+ metadata[:toolbox].output("[NOOP] Assert: `#{object.cmd}` returns 0 #{interval_msg}")
19
+ if object.timeout > 0 || object.attempts > 0
20
+ timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
21
+ attempts_msg = object.attempts > 0 ? "#{object.attempts} attempts" : nil
22
+ giveup_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, give up..."
23
+ metadata[:toolbox].output(giveup_msg)
24
+ if object.timeout_statement
25
+ object.timeout_statement.parent = object.parent
26
+ object.timeout_statement.run(self, metadata.dup)
27
+ end
28
+ metadata[:toolbox].output("and exit")
29
+ end
30
+ return
31
+ end
32
+
33
+ gave_up = false
34
+ test_args = ssh_kit_command(object.cmd, raw: object.cmd_raw)
35
+ test_options = ssh_kit_command_options(cmd_ssh_config)
36
+
37
+ with_ssh_config(cmd_ssh_config) do
38
+ time = Time.now
39
+ count = object.attempts
40
+ while !(test(*test_args, test_options))
41
+ if ((count -= 1) == 0)
42
+ gave_up = true
43
+ break
44
+ end
45
+
46
+ if (object.timeout > 0 && Time.now - time > object.timeout)
47
+ gave_up = true
48
+ break
49
+ end
50
+
51
+ sleep(object.interval)
52
+ end
53
+ end
54
+
55
+ if gave_up
56
+ error_msg = "Error! Assertion `#{object.cmd}` failed"
57
+ metadata[:toolbox].error(error_msg)
58
+ if object.timeout_statement
59
+ object.timeout_statement.parent = object.parent
60
+ object.timeout_statement.run(self, metadata.dup)
61
+ end
62
+ raise Runbook::Runner::ExecutionError, error_msg
63
+ end
64
+ end
65
+
66
+ def runbook__statements__capture(object, metadata)
67
+ _handle_capture(object, metadata) do |ssh_config, capture_args, capture_options|
68
+ if (ssh_config[:servers].size > 1)
69
+ warn_msg = "Warning: `capture` does not support multiple servers. Use `capture_all` instead.\n"
70
+ metadata[:toolbox].warn(warn_msg)
71
+ end
72
+
73
+ result = ""
74
+ with_ssh_config(ssh_config) do
75
+ result = capture(*capture_args, capture_options)
76
+ end
77
+ result
78
+ end
79
+ end
80
+
81
+ def runbook__statements__capture_all(object, metadata)
82
+ _handle_capture(object, metadata) do |ssh_config, capture_args, capture_options|
83
+ result = {}
84
+ mutex = Mutex.new
85
+ with_ssh_config(ssh_config) do
86
+ hostname = self.host.hostname
87
+ capture_result = capture(*capture_args, capture_options)
88
+ mutex.synchronize { result[hostname] = capture_result }
89
+ end
90
+ result
91
+ end
92
+ end
93
+
94
+ def _handle_capture(object, metadata, &block)
95
+ ssh_config = find_ssh_config(object)
96
+
97
+ if metadata[:noop]
98
+ ssh_config_output = render_ssh_config_output(ssh_config)
99
+ metadata[:toolbox].output(ssh_config_output) unless ssh_config_output.empty?
100
+ metadata[:toolbox].output("[NOOP] Capture: `#{object.cmd}` into #{object.into}")
101
+ return
102
+ end
103
+
104
+ metadata[:toolbox].output("\n") # for formatting
105
+
106
+ capture_args = ssh_kit_command(object.cmd, raw: object.raw)
107
+ capture_options = ssh_kit_command_options(ssh_config)
108
+ capture_options[:strip] = object.strip
109
+ capture_options[:verbosity] = Logger::INFO
110
+
111
+ capture_msg = "Capturing output of `#{object.cmd}`\n\n"
112
+ metadata[:toolbox].output(capture_msg)
113
+
114
+ result = block.call(ssh_config, capture_args, capture_options)
115
+
116
+ target = object.parent.dsl
117
+ target.singleton_class.class_eval { attr_accessor object.into }
118
+ target.send("#{object.into}=".to_sym, result)
119
+ end
120
+
121
+ def runbook__statements__command(object, metadata)
122
+ ssh_config = find_ssh_config(object)
123
+
124
+ if metadata[:noop]
125
+ ssh_config_output = render_ssh_config_output(ssh_config)
126
+ metadata[:toolbox].output(ssh_config_output) unless ssh_config_output.empty?
127
+ metadata[:toolbox].output("[NOOP] Run: `#{object.cmd}`")
128
+ return
129
+ end
130
+
131
+ metadata[:toolbox].output("\n") # for formatting
132
+
133
+ execute_args = ssh_kit_command(object.cmd, raw: object.raw)
134
+ exec_options = ssh_kit_command_options(ssh_config)
135
+
136
+ with_ssh_config(ssh_config) do
137
+ execute(*execute_args, exec_options)
138
+ end
139
+ end
140
+
141
+ def runbook__statements__download(object, metadata)
142
+ ssh_config = find_ssh_config(object)
143
+
144
+ if metadata[:noop]
145
+ ssh_config_output = render_ssh_config_output(ssh_config)
146
+ metadata[:toolbox].output(ssh_config_output) unless ssh_config_output.empty?
147
+ options = object.options
148
+ to = " to #{object.to}" if object.to
149
+ opts = " with options #{options}" unless options == {}
150
+ noop_msg = "[NOOP] Download: #{object.from}#{to}#{opts}"
151
+ metadata[:toolbox].output(noop_msg)
152
+ return
153
+ end
154
+
155
+ metadata[:toolbox].output("\n") # for formatting
156
+
157
+ with_ssh_config(ssh_config) do
158
+ download!(object.from, object.to, object.options)
159
+ end
160
+ end
161
+
162
+ def runbook__statements__upload(object, metadata)
163
+ ssh_config = find_ssh_config(object)
164
+
165
+ if metadata[:noop]
166
+ ssh_config_output = render_ssh_config_output(ssh_config)
167
+ metadata[:toolbox].output(ssh_config_output) unless ssh_config_output.empty?
168
+ options = object.options
169
+ to = " to #{object.to}" if object.to
170
+ opts = " with options #{options}" unless options == {}
171
+ noop_msg = "[NOOP] Upload: #{object.from}#{to}#{opts}"
172
+ metadata[:toolbox].output(noop_msg)
173
+ return
174
+ end
175
+
176
+ metadata[:toolbox].output("\n") # for formatting
177
+
178
+ with_ssh_config(ssh_config) do
179
+ upload!(object.from, object.to, object.options)
180
+ end
181
+ end
182
+ end
183
+
184
+ extend ClassMethods
185
+ end
186
+ end
@@ -0,0 +1,22 @@
1
+ module Runbook
2
+ class Statement < Node
3
+ include Runbook::Hooks::Invoker
4
+
5
+ attr_accessor :parent
6
+
7
+ def render(view, output, metadata)
8
+ invoke_with_hooks(view, self, output, metadata) do
9
+ view.render(self, output, metadata)
10
+ end
11
+ end
12
+
13
+ def run(run, metadata)
14
+ return if dynamic? && visited?
15
+
16
+ invoke_with_hooks(run, self, metadata) do
17
+ run.execute(self, metadata)
18
+ end
19
+ self.visited!
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Runbook::Statements
2
+ class Ask < Runbook::Statement
3
+ attr_reader :prompt, :into, :default
4
+
5
+ def initialize(prompt, into:, default: nil)
6
+ @prompt = prompt
7
+ @into = into
8
+ @default = default
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ module Runbook::Statements
2
+ class Assert < Runbook::Statement
3
+ attr_reader :cmd, :cmd_ssh_config, :cmd_raw
4
+ attr_reader :interval, :timeout, :attempts
5
+ attr_reader :timeout_statement
6
+
7
+ def initialize(
8
+ cmd,
9
+ cmd_ssh_config: nil,
10
+ cmd_raw: false,
11
+ interval: 1,
12
+ timeout: 0,
13
+ attempts: 0,
14
+ timeout_statement: nil
15
+ )
16
+ @cmd = cmd
17
+ @cmd_ssh_config = cmd_ssh_config
18
+ @cmd_raw = cmd_raw
19
+ @interval = interval
20
+ @timeout = timeout
21
+ @attempts = attempts
22
+ @timeout_statement = timeout_statement
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module Runbook::Statements
2
+ class Capture < Runbook::Statement
3
+ attr_reader :cmd, :into, :ssh_config, :raw, :strip
4
+
5
+ def initialize(cmd, into:, ssh_config: nil, raw: false, strip: true)
6
+ @cmd = cmd
7
+ @into = into
8
+ @ssh_config = ssh_config
9
+ @raw = raw
10
+ @strip = strip
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,14 @@
1
+ module Runbook::Statements
2
+ class CaptureAll < Runbook::Statement
3
+ attr_reader :cmd, :into, :ssh_config, :raw, :strip
4
+
5
+ def initialize(cmd, into:, ssh_config: nil, raw: false, strip: true)
6
+ @cmd = cmd
7
+ @into = into
8
+ @ssh_config = ssh_config
9
+ @raw = raw
10
+ @strip = strip
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,11 @@
1
+ module Runbook::Statements
2
+ class Command < Runbook::Statement
3
+ attr_reader :cmd, :ssh_config, :raw
4
+
5
+ def initialize(cmd, ssh_config: nil, raw: false)
6
+ @cmd = cmd
7
+ @ssh_config = ssh_config
8
+ @raw = raw
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Runbook::Statements
2
+ class Confirm < Runbook::Statement
3
+ attr_reader :prompt
4
+
5
+ def initialize(prompt)
6
+ @prompt = prompt
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,9 @@
1
+ module Runbook::Statements
2
+ class Description < Runbook::Statement
3
+ attr_reader :msg
4
+
5
+ def initialize(msg)
6
+ @msg = msg
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Runbook::Statements
2
+ class Download < Runbook::Statement
3
+ attr_reader :from, :to, :options, :ssh_config
4
+
5
+ def initialize(from, to: nil, ssh_config: nil, options: {})
6
+ @from = from
7
+ @to = to
8
+ @ssh_config = ssh_config
9
+ @options = options
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Runbook::Statements
2
+ class Layout < Runbook::Statement
3
+ attr_reader :structure
4
+
5
+ def initialize(structure)
6
+ @structure = structure
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,10 @@
1
+ module Runbook::Statements
2
+ class Note < Runbook::Statement
3
+ attr_reader :msg
4
+
5
+ def initialize(msg)
6
+ @msg = msg
7
+ end
8
+ end
9
+ end
10
+