runbook 0.12.1

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.
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
+