runbook 0.14.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +17 -0
- data/.gitignore +4 -0
- data/.ruby-version +1 -1
- data/.travis.yml +23 -7
- data/Appraisals +8 -0
- data/CHANGELOG.md +71 -0
- data/README.md +159 -25
- data/Rakefile +7 -1
- data/TODO.md +316 -46
- data/dockerfiles/Dockerfile-runbook +18 -0
- data/dockerfiles/Dockerfile-sshd +4 -0
- data/{samples → examples}/hooks_runbook.rb +0 -0
- data/{samples → examples}/layout_runbook.rb +0 -0
- data/{samples → examples}/restart_nginx.rb +0 -0
- data/{samples → examples}/simple_runbook.rb +0 -0
- data/examples/suppress_capture_output.rb +47 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/activesupport_5.gemfile +7 -0
- data/gemfiles/activesupport_6.gemfile +7 -0
- data/lib/runbook.rb +28 -6
- data/lib/runbook/airbrussh_context.rb +25 -0
- data/lib/runbook/cli.rb +17 -13
- data/lib/runbook/configuration.rb +7 -1
- data/lib/runbook/entities/book.rb +2 -2
- data/lib/runbook/entities/section.rb +2 -2
- data/lib/runbook/entities/setup.rb +7 -0
- data/lib/runbook/entities/step.rb +2 -2
- data/lib/runbook/entity.rb +7 -5
- data/lib/runbook/extensions/add.rb +1 -0
- data/lib/runbook/extensions/sections.rb +6 -2
- data/lib/runbook/extensions/setup.rb +17 -0
- data/lib/runbook/extensions/ssh_config.rb +2 -0
- data/lib/runbook/extensions/statements.rb +6 -1
- data/lib/runbook/extensions/steps.rb +12 -2
- data/lib/runbook/generators/project/project.rb +32 -9
- data/lib/runbook/helpers/tmux_helper.rb +6 -4
- data/lib/runbook/node.rb +10 -0
- data/lib/runbook/run.rb +14 -8
- data/lib/runbook/runner.rb +2 -0
- data/lib/runbook/runs/ssh_kit.rb +20 -13
- data/lib/runbook/statement.rb +0 -2
- data/lib/runbook/statements/ask.rb +3 -2
- data/lib/runbook/statements/assert.rb +11 -2
- data/lib/runbook/toolbox.rb +3 -9
- data/lib/runbook/util/repo.rb +4 -3
- data/lib/runbook/util/runbook.rb +4 -0
- data/lib/runbook/util/stored_pose.rb +4 -3
- data/lib/runbook/version.rb +1 -1
- data/lib/runbook/views/markdown.rb +15 -7
- data/runbook.gemspec +12 -8
- metadata +108 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11c80dcba3596671291c8d9dbd40a33ae5691963b27dffdd7459cb4f4ebd94be
|
4
|
+
data.tar.gz: a8a578797429344e1fa29c84cb395db5c9c725f4b35b00440e26708328fdca26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bda3ee51331bcac0c38f338e4ddcf001847328f07093ff1163b8a8b4394e523b116c05b88de449d7072dc90fbe51da3049f00778933a8e8d0b8d3eac39c935c0
|
7
|
+
data.tar.gz: 11420133d97afa081df1105bbf3dc1364b5722aa50e57f49777f761c2df88b152b50b4db80c2da81a8d10328bc85b46de51721a010113d26b965411243a51e68
|
data/.dockerignore
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
**/.git
|
2
|
+
/.bundle/
|
3
|
+
/.yardoc
|
4
|
+
/Gemfile.lock
|
5
|
+
/_yardoc/
|
6
|
+
/coverage/
|
7
|
+
/doc/
|
8
|
+
/pkg/
|
9
|
+
/spec/reports/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
# rspec failure tracking
|
13
|
+
.rspec_status
|
14
|
+
|
15
|
+
# recommended:
|
16
|
+
# https://github.com/thoughtbot/appraisal#version-control
|
17
|
+
gemfiles/*.gemfile.lock
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.5.5
|
data/.travis.yml
CHANGED
@@ -1,9 +1,25 @@
|
|
1
|
-
|
1
|
+
dist: xenial
|
2
2
|
language: ruby
|
3
|
+
services:
|
4
|
+
- docker
|
3
5
|
rvm:
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
- 2.
|
9
|
-
|
6
|
+
- 2.3.8
|
7
|
+
- 2.4.10
|
8
|
+
- 2.5.8
|
9
|
+
- 2.6.6
|
10
|
+
- 2.7.2
|
11
|
+
- 3.0.0
|
12
|
+
gemfile:
|
13
|
+
- gemfiles/activesupport_5.gemfile
|
14
|
+
- gemfiles/activesupport_6.gemfile
|
15
|
+
matrix:
|
16
|
+
exclude:
|
17
|
+
- rvm: 2.3.8
|
18
|
+
gemfile: gemfiles/activesupport_6.gemfile
|
19
|
+
- rvm: 2.4.10
|
20
|
+
gemfile: gemfiles/activesupport_6.gemfile
|
21
|
+
|
22
|
+
before_install: gem install bundler -v 2.2.16
|
23
|
+
|
24
|
+
script:
|
25
|
+
- bundle exec rake spec_all
|
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,77 @@ This log maintains a list of all substantive changes to Runbook. The log include
|
|
4
4
|
|
5
5
|
## master
|
6
6
|
|
7
|
+
## `v1.1.0` (2021-05-21)
|
8
|
+
|
9
|
+
### Breaking Changes:
|
10
|
+
|
11
|
+
* Removes `runbook install` (should have happened in v1.0.0)
|
12
|
+
* Drops support for Ruby 2.2 and bundler < 2.2
|
13
|
+
|
14
|
+
### Features:
|
15
|
+
|
16
|
+
* Add support for Ruby 3.0.0 (thanks @pblesi!)
|
17
|
+
* Add metadata argument that saves TMUX panes: keep_panes (thanks @ClashTheBunny)
|
18
|
+
|
19
|
+
### Documentation:
|
20
|
+
|
21
|
+
* Update assert parameters description
|
22
|
+
* Update example for module class method (thanks @voodoologic!)
|
23
|
+
|
24
|
+
## `v1.0.0` (2020-07-24)
|
25
|
+
|
26
|
+
### Breaking Changes:
|
27
|
+
|
28
|
+
* Commands and tmux commands that previously escaped single quotes will now require un-escaped single quotes
|
29
|
+
|
30
|
+
### Features:
|
31
|
+
|
32
|
+
* Add Node#parent_entity to find the containinng entity for a node
|
33
|
+
|
34
|
+
### Fixes:
|
35
|
+
|
36
|
+
* Fix bugs requiring escaping single quotes in commands and tmux_commands (BACKWARDS INCOMPATIBLE CHANGE)
|
37
|
+
* Fix `File.exists?` deprecation warning (Thanks onk!)
|
38
|
+
|
39
|
+
### Documentation:
|
40
|
+
|
41
|
+
* Add suppress_capture_output.rb runbook example
|
42
|
+
|
43
|
+
## `v0.16.1` (2019-11-25)
|
44
|
+
|
45
|
+
### Fixes:
|
46
|
+
|
47
|
+
* Fix bug preventing skipping of steps not nested in sections (Thanks celeen!)
|
48
|
+
|
49
|
+
## `v0.16.0` (2019-11-22)
|
50
|
+
|
51
|
+
### Fixes:
|
52
|
+
|
53
|
+
* Add better error messages for runtime values accessed at compile time
|
54
|
+
|
55
|
+
### New Features
|
56
|
+
|
57
|
+
* Add entity tags and labels
|
58
|
+
* Add `setup` entity for initial runbook setup code
|
59
|
+
* Add `Runbook.views` method for accessing an array of all defined views
|
60
|
+
* Add airbrussh context for better ssh_kit output
|
61
|
+
* Backtick "into" targets in markdown view output (Thanks fwolfst!)
|
62
|
+
|
63
|
+
## `v0.15.0` (2019-09-29)
|
64
|
+
|
65
|
+
### Fixes:
|
66
|
+
|
67
|
+
* Halt the project generator if gem generation fails
|
68
|
+
* Replace timeout_statement with abort_statement for assert statements
|
69
|
+
* Make runbook state files only readable by the current user
|
70
|
+
* Allow / characters in the title of a runbook (Thanks brafales!)
|
71
|
+
|
72
|
+
### New Features
|
73
|
+
|
74
|
+
* Allow books to have steps as children
|
75
|
+
* Allow "echo: false" for ask statements
|
76
|
+
* Expose "run" as an argument to ruby_commands
|
77
|
+
|
7
78
|
## `v0.14.0` (2019-08-15)
|
8
79
|
|
9
80
|
### Fixes:
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/runbook.svg)](https://badge.fury.io/rb/runbook)
|
4
4
|
[![Build Status](https://travis-ci.org/braintree/runbook.svg?branch=master)](https://travis-ci.org/braintree/runbook)
|
5
|
+
[![Gitter](https://badges.gitter.im/braintree/runbook.svg)](https://gitter.im/braintree/runbook?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
5
6
|
|
6
7
|
_See our [blog post](https://medium.com/braintree-product-technology/https-medium-com-braintree-product-technology-runbook-be6f072cfc0d) for the philosophy behind Runbook and an overview of its features._
|
7
8
|
|
@@ -118,10 +119,12 @@ Initialize Runbook in your project:
|
|
118
119
|
|
119
120
|
* [1. Runbook Anatomy](#runbook-anatomy)
|
120
121
|
* [1.1 Entities, Statements, and Setters](#entities-statements-and-setters)
|
121
|
-
* [1.1.1 Books, Sections, and
|
122
|
+
* [1.1.1 Books, Sections, Steps, and Setup](#books-sections-steps-and-setup)
|
122
123
|
* [1.1.1.1 Books](#books)
|
123
124
|
* [1.1.1.2 Sections](#sections)
|
124
125
|
* [1.1.1.3 Steps](#steps)
|
126
|
+
* [1.1.1.4 Setup](#setup)
|
127
|
+
* [1.1.1.5 Tags and Labels](#tags-and-labels)
|
125
128
|
* [1.1.2 Statements](#statements)
|
126
129
|
* [1.1.2.1 Ask](#ask)
|
127
130
|
* [1.1.2.2 Assert](#assert)
|
@@ -169,11 +172,12 @@ Initialize Runbook in your project:
|
|
169
172
|
* [6.8 Adding to Runbook's Configuration](#adding-to-runbooks-configuration)
|
170
173
|
* [7. Testing](#testing)
|
171
174
|
* [8. Known Issues](#known-issues)
|
172
|
-
* [9.
|
173
|
-
* [10.
|
174
|
-
* [11.
|
175
|
-
* [12.
|
176
|
-
* [13.
|
175
|
+
* [9. FAQ](#faq)
|
176
|
+
* [10. Development](#development)
|
177
|
+
* [11. Contributing](#contributing)
|
178
|
+
* [12. Feature Requests](#feature-requests)
|
179
|
+
* [13. License](#license)
|
180
|
+
* [14. Code of Conduct](#code-of-conduct)
|
177
181
|
|
178
182
|
## Runbook Anatomy
|
179
183
|
|
@@ -220,7 +224,7 @@ Hierarchically, a runbook looks like this:
|
|
220
224
|
|
221
225
|
A runbook is composed of entities, statements, and setters. Runbook entities contain either other entities or statements. Examples of entities include Books, Sections, and Steps. They define the structure of the runbook and can be considered the "nodes" of the tree structure. As entities are the nodes of the tree structure, statements are the "leaves" of the structure and comprise the various behaviors or commands of the runbook. Setters, typically referenced from within steps, associate state with the node, which can be accessed by its children.
|
222
226
|
|
223
|
-
#### Books, Sections, and
|
227
|
+
#### Books, Sections, Steps, and Setup
|
224
228
|
|
225
229
|
Entities are composed of a title and a list of items which are their children. Each entity can be rendered with a specific view or executed with a specific run.
|
226
230
|
|
@@ -233,7 +237,7 @@ Runbook.book "Unbalance node" do
|
|
233
237
|
end
|
234
238
|
```
|
235
239
|
|
236
|
-
Every book requires a title. Books can have description, layout, and
|
240
|
+
Every book requires a title. Books can have description, layout, section, and step children. Descriptions describe the book and are declared with the `description` keyword.
|
237
241
|
|
238
242
|
##### Sections
|
239
243
|
|
@@ -243,31 +247,91 @@ A book is broken up into sections. Every section requires a title. Sections can
|
|
243
247
|
|
244
248
|
Steps hold state and group together a set of statements. Steps do not require titles or children. This allows runbooks to be very flexible. You can fill out steps as needed, or be terse when the behavior of the step is self-evident. Steps without titles will not prompt to continue when running in paranoid mode.
|
245
249
|
|
250
|
+
##### Setup
|
251
|
+
|
252
|
+
Setup is a special entity that is always executed. It is not skipped when starting or resuming execution in the middle of a runbook. A prompt is never presented to determine if you should or should not execute the setup section. The setup section is similar to the step entity in that it shares the same DSL. In other words, any keywords available within steps are also available within the setup section.
|
253
|
+
|
254
|
+
The setup section provides two important use cases. First, it allows you ensure any dependent values are defined when executing your runbook. If skipping the initial steps of your runbook and starting in the middle, you can be sure that any initialization steps have been executed. For example, presume you have a runbook that prompts for a list of servers, stops the servers, and then starts them. It would be advantageous to define the prompting server logic in a setup section, so you can start the runbook at the start server step and know that the list of servers is defined.
|
255
|
+
|
256
|
+
Second, if you dynamically define the sections and steps in your runbook based on user input, then doing this in the setup section allows you start the runbook in the middle of the dynamically defined steps.
|
257
|
+
|
258
|
+
Because the setup section is always executed, it's execution should be idempotent. In other words, the setup section should be able to be executed multiple times in a row and produce the same result.
|
259
|
+
|
260
|
+
It may be ideal to ensure user input is only asked for once when executing a setup section.
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
Runbook.book "Make Pizza" do
|
264
|
+
setup do
|
265
|
+
ruby_command do
|
266
|
+
@toppings ||= ENV["TOPPINGS"]
|
267
|
+
ask "What toppings would you like?", into: :toppings, default: "cheese" unless @toppings
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
273
|
+
The above example will set `@toppings` from a passed-in environment variable if present, otherwise it will ask the user to set `@toppings`. If toppings have already has been defined from a previous execution, it will not prompt the user for the value again. Because this logic references a value that is defined at runtime (`@toppings`), it must be wrapped in a `ruby_command`.
|
274
|
+
|
275
|
+
##### Tags and Labels
|
276
|
+
|
277
|
+
Any entity can be associated with arbitrary tags or labels. Once tags or labels are assigned, entity behavior can be modified using [hooks](#augmenting-functionality-with-hooks).
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
Runbook.book "Bounce Nodes", :untested do
|
281
|
+
step "Disable monitoring", :skip do
|
282
|
+
confirm "Have you disabled health monitoring?"
|
283
|
+
end
|
284
|
+
|
285
|
+
step "Restart nodes", :aws_only, :mutator, labels: {rails_env: :production} do
|
286
|
+
confirm "Have you restarted the nodes?"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
```
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
Runbook::Runs::SSHKit.register_hook(:warn_for_untested_runbook, :before, Runbook::Entities::Book) do |object, metadata|
|
293
|
+
warning = "This runbook has not yet been tested. Beware of bugs!"
|
294
|
+
metadata[:toolbox].warn(warning) if object.tags.include?(:untested)
|
295
|
+
end
|
296
|
+
|
297
|
+
Runbook::Runs::SSHKit.register_hook(:skip_skippable_entities, :around, Runbook::Entity) do |object, metadata, block|
|
298
|
+
next if object.tags.include?(:skip)
|
299
|
+
next if object.labels[:rails_env] && object.labels[:rails_env] != ENV["RAILS_ENV"]
|
300
|
+
block.call
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
246
304
|
#### Statements
|
247
305
|
|
248
306
|
Statements are the workhorses of runbooks. They comprise all the behavior runbooks execute. Runbook comes with the following statements:
|
249
307
|
|
250
308
|
##### Ask
|
251
309
|
|
252
|
-
Prompts the user for a string and stores its value on the containing step entity. Once this statement is executed, its value is accessed as an instance variable under the `into` parameter. This value can be referenced in later statements such as the `ruby_command` statement.
|
310
|
+
Prompts the user for a string and stores its value on the containing step entity. Once this statement is executed, its value is accessed as an instance variable under the `into` parameter. This value can be referenced in later statements such as the `ruby_command` statement. An optional `default` value can be specified. An optional `echo` parameter can be specified to indicate whether typed input should be echoed to the screen.
|
253
311
|
|
254
312
|
```ruby
|
255
|
-
ask "What percentage of requests are failing?", into: :failing_request_percentage, default: "100"
|
313
|
+
ask "What percentage of requests are failing?", into: :failing_request_percentage, default: "100", echo: true
|
314
|
+
|
315
|
+
ruby_command do
|
316
|
+
note "Failing request percentage: #{@failing_request_percentage}"
|
317
|
+
end
|
256
318
|
```
|
257
319
|
|
320
|
+
In the above example, the `note` statement must be wrapped in a `ruby_command` statement. Without wrapping `note` in a `ruby_command`, it would be evaluated at compile time but the user will only be asked for input when the runbook is executed (so `@failing_request_percentage` would not have a value). If you find yourself wrapping many or all runbook statements in ruby commands it may make sense to set these values at compile time using environment variables.
|
321
|
+
|
258
322
|
##### Assert
|
259
323
|
|
260
|
-
Runs the provided `cmd` repeatedly until it returns true. A timeout and maximum number of attempts can be set.
|
324
|
+
Runs the provided `cmd` repeatedly until it returns true. A timeout and maximum number of attempts can be set. When either the attempt or timeout limit is hit, a command can be specified that will be run. If no command is specified, the process will fail. Commands can optionally be specified as `raw`. This tells SSHKit to not perform auto-wrapping of the commands, but execute the exact string on the remote server. See SSHKit's documentation for more details.
|
261
325
|
|
262
326
|
```ruby
|
263
327
|
assert(
|
264
328
|
'service nginx status | grep "is running"',
|
265
329
|
cmd_ssh_config: {servers: ["host1.prod"], parallelization: {strategy: :parallel}},
|
266
330
|
cmd_raw: false,
|
267
|
-
interval: 3,
|
268
|
-
timeout: 300,
|
269
|
-
attempts: 3,
|
270
|
-
|
331
|
+
interval: 3, # How often, in seconds, to wait between tries
|
332
|
+
timeout: 300, # Total time, in seconds, to keep trying command, after which it will fail
|
333
|
+
attempts: 3, # Total number of attempts after which the process will fail
|
334
|
+
abort_statement: Runbook::Statements::Command.new(
|
271
335
|
"echo 'help' | mail -s 'need help' page-me@page-me.com",
|
272
336
|
ssh_config: {servers: [:local], parallelization: {strategy: :parallel}},
|
273
337
|
raw: false
|
@@ -277,7 +341,7 @@ assert(
|
|
277
341
|
|
278
342
|
##### Capture
|
279
343
|
|
280
|
-
Runs the provided `cmd` and captures its output into `into`. An optional `ssh_config` can be specified to configure how the capture command gets run. Capture commands take an optional `strip` parameter that indicates if the returned output should have leading and trailing whitespace removed. Capture commands also take an optional `raw` parameter that tells SSHKit whether the command should be executed as is, or to include the auto-wrapping of the ssh_config.
|
344
|
+
Runs the provided `cmd` and captures its output into `into`. Once captured, this value can be referenced in later statements such as the `ruby_command` statement. An optional `ssh_config` can be specified to configure how the capture command gets run. Capture commands take an optional `strip` parameter that indicates if the returned output should have leading and trailing whitespace removed. Capture commands also take an optional `raw` parameter that tells SSHKit whether the command should be executed as is, or to include the auto-wrapping of the ssh_config.
|
281
345
|
|
282
346
|
```ruby
|
283
347
|
capture %Q{wc -l file.txt | cut -d " " -f 1}, into: :num_lines, strip: true, ssh_config: {user: "root"}
|
@@ -323,7 +387,7 @@ DESC
|
|
323
387
|
Downloads the specified file to `to`. An optional `ssh_config` can be specified to configure how the download command gets run, for example specifying the remote host and remote directory to download from. Optional `options` can be specified that get passed down to the underlying sshkit implementation
|
324
388
|
|
325
389
|
```ruby
|
326
|
-
download /home/pblesi/rad_file.txt, to: my_rad_file.txt, ssh_config: {servers: ["host1.prod"]}, options: {log_percent: 10}
|
390
|
+
download '/home/pblesi/rad_file.txt', to: my_rad_file.txt, ssh_config: {servers: ["host1.prod"]}, options: {log_percent: 10}
|
327
391
|
```
|
328
392
|
|
329
393
|
##### Layout
|
@@ -355,10 +419,10 @@ notice "There be dragons!"
|
|
355
419
|
|
356
420
|
##### Ruby Command
|
357
421
|
|
358
|
-
Executes its block in the context of the parent step. The block is passed the ruby_command statement
|
422
|
+
Executes its block in the context of the parent step. The block is passed the ruby_command statement, the execution metadata, and the run as arguments.
|
359
423
|
|
360
424
|
```ruby
|
361
|
-
ruby_command do |rb_cmd, metadata|
|
425
|
+
ruby_command do |rb_cmd, metadata, run|
|
362
426
|
if (failure_rate = rb_cmd.parent.failing_request_percentage) > 25
|
363
427
|
`echo 'Help! failure rate at #{failure_rate}' | mail -s 'High failure rate!' page-me@page-me.com`
|
364
428
|
else
|
@@ -379,6 +443,7 @@ Metadata at execution time is structured as follows:
|
|
379
443
|
noop: false, # A boolean indicating if you are running in noop mode. ruby_command blocks are never evaluated in noop mode
|
380
444
|
auto: false, # A boolean indicating if you are running in auto mode
|
381
445
|
paranoid: true, # A boolean indicating if you are running in paranoid mode (prompting before each step)
|
446
|
+
keep_panes: false, # A boolean indicating whether panes should be kept open after completion
|
382
447
|
start_at: 0, # A string representing the step where nodes should start being processed
|
383
448
|
toolbox: Runbook::Toolbox.new, # A collection of methods to invoke side-effects such as printing and collecting input
|
384
449
|
layout_panes: {}, # A map of pane names to pane ids. `layout_panes` is used by the `tmux_command` to identify which tmux pane to send the command to
|
@@ -789,7 +854,7 @@ step "Inspect plate" do
|
|
789
854
|
when "carrots"
|
790
855
|
add carrots_book
|
791
856
|
when "peas"
|
792
|
-
system("runbook exec
|
857
|
+
system("runbook exec examples/print_peas.rb")
|
793
858
|
else
|
794
859
|
metadata[:toolbox].warn("Found #{veggie}!")
|
795
860
|
end
|
@@ -864,7 +929,7 @@ Runbook can be extended to add custom functionality.
|
|
864
929
|
|
865
930
|
### Adding New Statements
|
866
931
|
|
867
|
-
In order to add a new statement to your DSL, create a class under `Runbook::Statements` that inherits from `Runbook::Statement`. This statement will be initialized with all arguments passed to the corresponding keyword in the DSL. Remember to also add a corresponding method to runs and views so your new statement can be
|
932
|
+
In order to add a new statement to your DSL, create a class under `Runbook::Statements` that inherits from `Runbook::Statement`. This statement will be initialized with all arguments passed to the corresponding keyword in the DSL. Remember to also add a corresponding method to runs and views so your new statement can be interpreted in each context.
|
868
933
|
|
869
934
|
```ruby
|
870
935
|
module Runbook::Statements
|
@@ -969,6 +1034,22 @@ end
|
|
969
1034
|
|
970
1035
|
When registering a hook, you specify the name of the hook, the type, and the statement or entity to add the hook to. `before` and `after` hooks execute the block before and after executing the entity or statement, respectively. `around` hooks take a block which executes the specified entity or statement. When specifying the class that the hook applies to, you can have the hook apply to all entities by specifying `Runbook::Entity`, all statements by specifying `Runbook::Statement`, or all items by specifying `Object`. Additionally, you can specify any specific entity or statement you would like the hook to apply to.
|
971
1036
|
|
1037
|
+
Hooks are defined on the run or view objects themselves. For example, you would register a hook with `Runbook::Runs::SSHKit` to have the hook be applied to the `SSHKit` run. You would register a hook with the `Runbook::Views::Markdown` view to have hooks apply to this view. If you want to apply a hook to all runs or views, you can use the `Runbook.runs` method or `Runbook.views` method to iterate through the runs or views respectively.
|
1038
|
+
|
1039
|
+
```ruby
|
1040
|
+
Runbook.runs.each do |run|
|
1041
|
+
run.register_hook(
|
1042
|
+
:give_words_of_encouragement,
|
1043
|
+
:before,
|
1044
|
+
Runbook::Entities::Book
|
1045
|
+
) do |object, metadata|
|
1046
|
+
metadata[:toolbox].output("You've got this!")
|
1047
|
+
end
|
1048
|
+
end
|
1049
|
+
```
|
1050
|
+
|
1051
|
+
Hooks can be defined anywhere prior to runbook execution. If defining a hook for only a single runbook, it makes sense to define the hook immediately prior to the runbook definition. If you want a hook to apply to all runbooks in your project, it can be defined in a config file such as the `Runbookfile`. If you want to selectively apply the hook to certain runbooks, it may make sense to define it in a file that can be required by runbooks when it is needed.
|
1052
|
+
|
972
1053
|
When starting at a certain position in the runbook, hooks for any preceding sections and steps will be skipped. After hooks will be run for a parent when starting at a child entity of a parent.
|
973
1054
|
|
974
1055
|
### Adding New Run Behaviors
|
@@ -1062,7 +1143,7 @@ Additionally, runbooks should contain their own assertions, sanity checks, monit
|
|
1062
1143
|
|
1063
1144
|
## Known Issues
|
1064
1145
|
|
1065
|
-
### Command Quoting
|
1146
|
+
### Command Quoting (Prior to v1.0)
|
1066
1147
|
|
1067
1148
|
Because ssh_config declarations such as `user`, `group`, `path`, `env`, and `umask` are implemented as wrappers around your provided commands, you must be aware that issues can arise if your commands contain characters such as single quotes that are not properly escaped.
|
1068
1149
|
|
@@ -1080,6 +1161,8 @@ command "echo '\\''I love cheese'\\''"
|
|
1080
1161
|
|
1081
1162
|
Alternatively, if you wish to avoid issues with SSHKit command wrapping, you can specify that your commands be executed in raw form, passed directly as written to the specified host.
|
1082
1163
|
|
1164
|
+
`tmux_command` wraps the input passed to it in single quotes. Therefore any single quotes passed to the `tmux_command` should be escaped using `'\\''`. This issue can manifest itself as part of the command not being echoed to the tmux pane.
|
1165
|
+
|
1083
1166
|
### Specifying env values
|
1084
1167
|
|
1085
1168
|
When specifying the `env` for running commands, if you place curly braces `{}` around the env values, it is required to enclose the arguments in parenthesis `()`, otherwise the following syntax error will result:
|
@@ -1106,21 +1189,72 @@ not as
|
|
1106
1189
|
env {rails_env: :production}
|
1107
1190
|
```
|
1108
1191
|
|
1192
|
+
## FAQ
|
1193
|
+
|
1194
|
+
### Are runbooks compiled?
|
1195
|
+
|
1196
|
+
Yes they are. When you define a runbook, a tree data structure is constructed much like an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). This is important because you do not have to worry about any side-effects such as executing server commands when this data structure is compiled. Once compiled, choosing either the run or view to execute the runbook object determines what behavior is executed at each node.
|
1197
|
+
|
1198
|
+
### Why are runbooks compiled?
|
1199
|
+
|
1200
|
+
Runbook is designed to minimize and mitigate issues that arise when running operations in production enviroments. One way this is accomplished is by compiling all statements in the runbook before execution is started. Validations and assertions can be made to reduce the likelihood that a runbook will encounter an error in the middle of an operation. In other words, Runbook provides some guarantees about proper formatting of a runbook before any commands execute that could affect live systems.
|
1201
|
+
|
1202
|
+
### Why is my variable/method not set?
|
1203
|
+
|
1204
|
+
Because runbooks are compiled, statements that set values such as `ask`, `capture`, and `capture_all` statements (using the `:into` keyword) only expose their values at runtime. This means any references to these methods or variables (specified with `:into`) can only happen within `ruby_command` blocks which are evaluated at runtime. If an argument to a statement references the values set by these statements, then the statement must be wrapped in a `ruby_command` block. See [Passing State](#passing-state) for specific examples.
|
1205
|
+
|
1206
|
+
### How do I define and call methods within a runbook?
|
1207
|
+
|
1208
|
+
When defining and referencing your own functions in a runbook, functions should be wrapped in a module so they can be referenced globally. For example:
|
1209
|
+
|
1210
|
+
```ruby
|
1211
|
+
module Adder
|
1212
|
+
def self.add(x, y)
|
1213
|
+
x + y
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
Runbook.book "Add Two Numbers" do
|
1218
|
+
step "Add numbers" do
|
1219
|
+
ask "X?", into: :x
|
1220
|
+
ask "Y?", into: :y
|
1221
|
+
|
1222
|
+
ruby_command do |rb_cmd, metadata, run|
|
1223
|
+
metadata[:toolbox].output("Result: #{Adder.add(x, y)}")
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
```
|
1228
|
+
|
1229
|
+
### Why does my command work on the command line but not in runbook?
|
1230
|
+
|
1231
|
+
There are a number of reasons why a command may work directly on your command line, but not when executed using the `command` statement. Some possible things to try include:
|
1232
|
+
|
1233
|
+
* Print the command with any variables substituted
|
1234
|
+
* Ensure the command works outside of runbook
|
1235
|
+
* Use full paths. The `PATH` environment variable may not be set.
|
1236
|
+
* Check for aliases. Aliases are usually not set for non-interactive shells.
|
1237
|
+
* Check for environment variables. Differences between your shell environment variables and those set for the executing shell may modify command behavior.
|
1238
|
+
* Check for differing behavior between the bourne shell (`sh`) and your shell (usually bash).
|
1239
|
+
* Check that quotes are properly being escaped.
|
1240
|
+
* Simplify the command you are executing and then slowly build it back up
|
1241
|
+
* Check for permissions issues that might cause different execution behavior.
|
1242
|
+
|
1109
1243
|
## Development
|
1110
1244
|
|
1111
1245
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
1112
1246
|
|
1113
1247
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
1114
1248
|
|
1115
|
-
To execute runbook using this repo, run `bundle exec exe/runbook exec
|
1249
|
+
To execute runbook using this repo, run `bundle exec exe/runbook exec examples/layout_runbook.rb`.
|
1116
1250
|
|
1117
1251
|
To release a new version:
|
1118
1252
|
|
1119
1253
|
1. Update the version number in `version.rb`.
|
1120
1254
|
2. Update the changelog in `CHANGELOG.rb`.
|
1121
1255
|
3. Commit changes with commit messsage: "Bump runbook version to X.Y.Z"
|
1122
|
-
4. Run `
|
1123
|
-
5.
|
1256
|
+
4. Run `gem signin` to ensure you can push the new version to rubygems.org
|
1257
|
+
5. Run `bundle exec rake release`, which will create a git tag for the version and push git commits and tags.
|
1124
1258
|
|
1125
1259
|
## Contributing
|
1126
1260
|
|