runbook 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cb2a40c659a212a9fda6d78d1e94b07ba51625896cd6e4f4a4b6668e42bbc4e
4
- data.tar.gz: 065708ff0b3bd93dfdcc35b60730138053e64af9e78768176282f5dc59236b66
3
+ metadata.gz: 9c05a6c35dcd51c2e91eabd838214a00c1bee394f372040c16f89d62d803a352
4
+ data.tar.gz: 44122acdd87e97bf0baafb59eed9bbea5be8109e5cd31afa6560da358b454a21
5
5
  SHA512:
6
- metadata.gz: 87559cf0bee8e17052a2639033a96481fba11963e50c596f138e64fc72b54328288202bfbe3576da6417eada36f219123ba54d726ab0cc6b28176f31552d21ae
7
- data.tar.gz: a0b414a4f0b8fa499cc46362d03d2dfb814cf65a46decbd796ffc45e7ecbd0f68db7a2a89168a3ace032814893ef649fa3a1d36982315f548688f4ce7e470b3c
6
+ metadata.gz: 252644365a05249254a5b4917fced2303d7554c136710adb100fcb7403157da5cb34d6a46d9b2ee9434f8fe89800330a16753cab634e35bb867142156903a706
7
+ data.tar.gz: 8d903da453e97dd258ff3f656f64540aa8674646b3c9a305e21cd9afd7018d41e8cf69aad8c9f43930f51ccce6b4cff5efeb6f3abca13c3216b96d7908108fbb
data/.gitignore CHANGED
@@ -10,3 +10,7 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+
14
+ # recommended:
15
+ # https://github.com/thoughtbot/appraisal#version-control
16
+ gemfiles/*.gemfile.lock
@@ -3,7 +3,22 @@ language: ruby
3
3
  rvm:
4
4
  - 2.2.3
5
5
  - 2.2.10
6
+ - 2.3.8
6
7
  - 2.4.6
7
8
  - 2.5.5
8
9
  - 2.6.3
10
+ gemfile:
11
+ - gemfiles/activesupport_5.gemfile
12
+ - gemfiles/activesupport_6.gemfile
13
+ matrix:
14
+ exclude:
15
+ - rvm: 2.2.3
16
+ gemfile: gemfiles/activesupport_6.gemfile
17
+ - rvm: 2.2.10
18
+ gemfile: gemfiles/activesupport_6.gemfile
19
+ - rvm: 2.3.8
20
+ gemfile: gemfiles/activesupport_6.gemfile
21
+ - rvm: 2.4.6
22
+ gemfile: gemfiles/activesupport_6.gemfile
23
+
9
24
  before_install: gem install bundler -v 1.15.4
@@ -0,0 +1,8 @@
1
+ appraise "activesupport-5" do
2
+ gem "activesupport", "~> 5.0"
3
+ end
4
+
5
+ appraise "activesupport-6" do
6
+ gem "activesupport", "~> 6.0"
7
+ end
8
+
@@ -4,6 +4,21 @@ This log maintains a list of all substantive changes to Runbook. The log include
4
4
 
5
5
  ## master
6
6
 
7
+ ## `v0.15.0` (2019-09-29)
8
+
9
+ ### Fixes:
10
+
11
+ * Halt the project generator if gem generation fails
12
+ * Replace timeout_statement with abort_statement for assert statements
13
+ * Make runbook state files only readable by the current user
14
+ * Allow / characters in the title of a runbook (Thanks brafales)
15
+
16
+ ### New Features
17
+
18
+ * Allow books to have steps as children
19
+ * Allow "echo: false" for ask statements
20
+ * Expose "run" as an argument to ruby_commands
21
+
7
22
  ## `v0.14.0` (2019-08-15)
8
23
 
9
24
  ### Fixes:
data/README.md CHANGED
@@ -233,7 +233,7 @@ Runbook.book "Unbalance node" do
233
233
  end
234
234
  ```
235
235
 
236
- Every book requires a title. Books can have description, layout, and section children. Descriptions describe the book and are declared with the `description` keyword.
236
+ 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
237
 
238
238
  ##### Sections
239
239
 
@@ -249,10 +249,10 @@ Statements are the workhorses of runbooks. They comprise all the behavior runboo
249
249
 
250
250
  ##### Ask
251
251
 
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.
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. 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
253
 
254
254
  ```ruby
255
- ask "What percentage of requests are failing?", into: :failing_request_percentage, default: "100"
255
+ ask "What percentage of requests are failing?", into: :failing_request_percentage, default: "100", echo: true
256
256
  ```
257
257
 
258
258
  ##### Assert
@@ -267,7 +267,7 @@ assert(
267
267
  interval: 3, # seconds
268
268
  timeout: 300, # seconds
269
269
  attempts: 3,
270
- timeout_statement: Runbook::Statements::Command.new(
270
+ abort_statement: Runbook::Statements::Command.new(
271
271
  "echo 'help' | mail -s 'need help' page-me@page-me.com",
272
272
  ssh_config: {servers: [:local], parallelization: {strategy: :parallel}},
273
273
  raw: false
@@ -355,10 +355,10 @@ notice "There be dragons!"
355
355
 
356
356
  ##### Ruby Command
357
357
 
358
- Executes its block in the context of the parent step. The block is passed the ruby_command statement and the execution metadata as arguments.
358
+ 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
359
 
360
360
  ```ruby
361
- ruby_command do |rb_cmd, metadata|
361
+ ruby_command do |rb_cmd, metadata, run|
362
362
  if (failure_rate = rb_cmd.parent.failing_request_percentage) > 25
363
363
  `echo 'Help! failure rate at #{failure_rate}' | mail -s 'High failure rate!' page-me@page-me.com`
364
364
  else
@@ -1119,8 +1119,8 @@ To release a new version:
1119
1119
  1. Update the version number in `version.rb`.
1120
1120
  2. Update the changelog in `CHANGELOG.rb`.
1121
1121
  3. Commit changes with commit messsage: "Bump runbook version to X.Y.Z"
1122
- 4. Run `bundle exec rake release`, which will create a git tag for the version and push git commits and tags.
1123
- 5. Push the `.gem` file in `pkg` to your gem repository
1122
+ 4. Run `gem signin` to ensure you can push the new version to rubygems.org
1123
+ 5. Run `bundle exec rake release`, which will create a git tag for the version and push git commits and tags.
1124
1124
 
1125
1125
  ## Contributing
1126
1126
 
data/TODO.md CHANGED
@@ -1,46 +1,279 @@
1
- ## Desired feature list
2
- * [X] Revise README.md
3
- * [X] Create a generator for a runbook? Allow for custom generators?
4
- * Generate runbook templates
5
- * Generate runbook projects with Runbookfile, Gemfile, etc.
6
- * Generate plugins
7
- * [] capistrano-runbook gem that integrates runbook into capistrano tasks
8
- * [] Add Appraisal to test against multiple versions of Ruby
9
- * [] Update output to be more log friendly, including timestamps for operations
10
- * [] Allow for preventing echo when prompting for input
11
- * [] Add an ability for skipping mutation commands that will have an affect on the system
12
- * [] Add a revert section that does not get executed, but can be executed by passing a revert flag
13
- * [] Specify version in runbook to allow for supporting backwards incompatible runbook DSL format changes
14
- * [] Add support for sudo interaction handler for raw commands
15
- * [] Replace Thor with a solution that is more easily extendable (adding new flags, etc.)
16
- * [] Add goto statements for repeating steps (functionality exists in paranoid mode)
17
- * [] Add test statement
18
- * [] Add ruby_assert statement
19
- * [] Feedback on completion of tmux commands (when they complete, return values, outputs)
20
- * [] Add shorter aliases for tmux layout keys
21
- * [] Add host aliases for ssh_config setters
22
- * [] Allow for step dependencies that get executed before the step
23
- * [] Add periodic flush for sshkit output
24
- * [] Update assert attribute nomenclature (timeout_statement)
25
- * [] Update ssh_kit to count commands separately between steps
26
- * [] Create a plugin generator
27
- * [] Create generator for a new run (sshkit, etc.)
28
- * [] Create generator for a new view (markdown, yaml, html, etc.)
29
- * Add rake tasks to generators for viewing and executing runbooks
30
- * Add a way to execute a series of commands in groups
31
- * Docker testing story for more full-stack integration tests
32
- * Test integration with sshkit
33
- * Test sshkit-sudo functionality
34
- * logging solution for alternate output
35
- * Pattern for conditionally enabling plugins? Conditional plugins should be implemented as separate gems.
36
- * Requiring a plugin is the same as enabling it
37
- * Configuration can be added to toggle aspects of the plugin
38
- * Add a setup step that always executes even if start_at is defined
39
- * Yaml specification format (subset of Ruby)
40
- * Will not contain as much flexibility as Ruby format
41
- * Can convert from Ruby format to yaml (depending on compatibility) and yaml to Ruby
42
- * Guard for view updates (How to handle arguments?)
43
- * Be able to serve up markdown docs (web server) for easy viewing
44
- * Add interactive executable for all runbooks in a project
45
- * Could provide a rake task for compiling and nooping runbooks?
46
- * Can specify input-format, output-format, input (file), and output (file)
1
+ # Runbook TODO
2
+
3
+ ## Description
4
+
5
+ This document covers a list of ideas for Runbook improvements. Ideas vary wildly in terms of difficulty, desireability, and conceptual completeness. Each idea has a difficulty, desireability, and conceptual completeness rating from 1 to 3. These ideas are roughly ordered in terms of priority.
6
+
7
+ Ratings are as follows:
8
+
9
+ Difficulty:
10
+
11
+ * 1: Easy
12
+ * 2: Medium
13
+ * 3: Hard
14
+
15
+ Desireability:
16
+
17
+ * 1: Must have
18
+ * 2: Nice to have
19
+ * 3: May be nice to have
20
+
21
+ Conceptual Completeness:
22
+
23
+ * 1: I know exactly how to implement this
24
+ * 2: I have some idea about how to implement this
25
+ * 3: I have no ideas about how to implement this
26
+
27
+ ## Other Considerations
28
+
29
+ Runbook is intended to be a light-weight, minimalistic library. This means every feature must be weighed against the usability of work-arounds in order to determine if a feature is worth including in Runbook. Sometimes features are easy and straightforward to implement but not enough demand has been demonstrated to justify including the feature into Runbook.
30
+
31
+ ## Features Outline
32
+
33
+ * [Sudo Raw Command Support](#sudo-raw-command-support): Add support for sudo interaction handler for raw commands
34
+ * [Always-executed Setup Section](#always-executed-setup-section): Add support for a section that is never skipped
35
+ * [Shortened Tmux Layout Keys](#shortened-tmux-layout-keys): Make tmux layout keys easier
36
+ * [Add Host Aliases for SSH Config](#add-host-aliases-for-ssh-config): Use `host` and `hosts` instead of `server` and `servers`
37
+ * [Capistrano-runbook Gem](#capistrano-runbook-gem): A gem for integrating runbook directly into capistrano
38
+ * [Blocking Tmux Commands](#blocking-tmux-commands): Don't continue execution until the tmux_command completes
39
+ * [Runbook Logger](#runbook-logger): A logger for Runbook
40
+ * [Mutation Command Skipping](#mutation-command-skipping): Allow skipping mutation commands
41
+ * [Revert Section](#revert-section): A section that can be triggered which will revert the changes of the runbook
42
+ * [Runbook Versioning](#runbook-versioning): Specify a version in a runbook to allow for supporting backwards incompatible runbook DSL format changes
43
+ * [Replace Thor CLI](#replace-thor-cli): Replace Thor for Runbook's CLI with something that can be more easily extended and customized
44
+ * [Goto Statement](#goto-statement): A statement for jumping to a specific step
45
+ * [Test Statement](#test-statement): A statement for executing a remote command and conditioning on its success
46
+ * [RubyAssert Statement](#rubyassert-statement): A statement analogous to assert, but executing a block instead of a command
47
+ * [Docker Testing](#docker-testing): Add tests that execute against docker containers
48
+ * [Tmux Command Results](#tmux-command-results): Capture outputs and status codes for tmux commands
49
+ * [Step Dependencies](#step-dependencies): Like rake, allow a step to invoke another step if it has not been executed
50
+ * [Update Command Counts](#update-command-counts): Use step titles for counting commands executed in a step
51
+ * [Create Plugin Generator](#create-plugin-generator): A generator for creating boilerplate for a new Runbook plugin
52
+ * [Create Run Generator](#create-run-generator): A generator for creating boilerplate for a new run such as sshkit
53
+ * [Create View Generator](#create-view-generator): A generator for creating boilerplate for a new view such as markdown, yaml, html
54
+ * [Outline View Option](#outline-view-option): A command-line option to only execute entity nodes
55
+ * [Guard Runbook](#guard-runbook): Automatically update Runbook views when a runbook is saved
56
+ * [Rake Task Interface](#rake-task-interface): Execute and view runbooks with rake tasks
57
+ * [Multiple Commands In Groups](#multiple-commands-in-groups): Execute multiple sshkit commands for a group of servers before moving on to a new group of servers
58
+ * [Yaml Specification](#yaml-specification): Define your runbook using yaml instead of Ruby
59
+ * [Runbook Web Server](#runbook-web-server): Automatically serve up Runbook views via the web
60
+ * [Interactive Runbook Launcher](#interactive-runbook-launcher): A CLI launcher to review and execute runbooks
61
+
62
+ ### Feature Details
63
+
64
+ #### Sudo Raw Command Support
65
+
66
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
67
+
68
+ Runbook supports a password prompt when executing commands using sudo. The typical way to execute sudo commands is to specify the `user` setter. This will do three things. It will (1) set the interaction handler when performing the initial [sudo check](https://github.com/braintree/runbook/blob/54e90f2a9c93704857bc31b0e03769b6e959d879/lib/hacks/ssh_kit.rb#L40-L49) and the pty for the execution of the command. It will (2) wrap the command to be executed with [a call to sudo](https://github.com/braintree/runbook#command-quoting). And it will (3) set the [interaction handler for commands](https://github.com/braintree/runbook/blob/54e90f2a9c93704857bc31b0e03769b6e959d879/lib/runbook/helpers/ssh_kit_helper.rb#L11-L13) that are executing which are wrapped in sudo.
69
+
70
+ Raw commands remove all the [command wrapping](https://github.com/braintree/runbook/commit/075d95c214c44db2c2803c211fbd40e1fbc89ae9#diff-a4bf760ce531af31e88293aecd750138https://github.braintreeps.com/braintree/runbook#command-quoting) that is performed when specifying commands. This is nice when you want to avoid the helper functionality when it complicates or obfuscates the command you are trying to execute (for example trying to escape nested quotes).
71
+
72
+ when raw is specified, number (!) above is not executed. Thus pty is not set to true. (2) can be assumed to manually be performed by the user (they will have to type their own sudo command). (3) Is currently toggled by the user setter and the `enable_sudo_prompt` config.
73
+
74
+ An ideal solution for this would allow the user to set pty true for the command to be executed via ssh_config. Additionally, it would be beneficial to set their own interaction handler. It seems safe to assume that if a user specifies that a command be executed with a pty, then we can set the sudo interaction handler by default if `enable_sudo_prompt` is set, but still allow for the interaction_handler to be overridden.
75
+
76
+ #### Always-executed Setup Section
77
+
78
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
79
+
80
+ It is often a best practice to provide a setup section at the beginning of your runbook which gathers all required info for the runbook so the rest of the runbook can execute with minimal interruption. Under certain circumstances it can be ideal to ensure this section is always run, so that if you want to jump to the middle of a runbook, you can have confidence that any necessary initial configuration is executed.
81
+
82
+ This can additionally be used if you want to dynamically define your runbook based on some initial input, then you can ensure that this is executed and you can step to the middle of a runbook, but know that the step has been defined by the initial setup. This, however, would not aid in generating a proper view for the runbook.
83
+
84
+ #### Shortened Tmux Layout Keys
85
+
86
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
87
+
88
+ `directory` should become `path` because it is shorter and consistent with the DSL. `runbook_pane` should become `runbook` because it is shorter and just as intuitive. Usage of old values should be marked as deprecated.
89
+
90
+ #### Add Host Aliases for SSH Config
91
+
92
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
93
+
94
+ `server` should become `host` because it is shorter. `servers` should become `hosts` because it is shorter and just as intuitive. Usage of old values should be marked as deprecated.
95
+
96
+ #### Capistrano-runbook Gem
97
+
98
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
99
+
100
+ A number of Ruby and non-Ruby projects use capistrano for deployments and other one-off tasks. Often times an application or organization's server inventory/manifest lives within capistrano. Creating a `capistrano-runbook` gem would (a) Allow you to access server lists programmatically within your runbooks based on role and (b) invoke capistrano tasks within the same Ruby process.
101
+
102
+ The implementation for this would be pretty straightforward. It would provide an alternative interface for invoking runbooks (as opposed to invoking via the CLI).
103
+
104
+ Helpful links:
105
+
106
+ * https://github.com/braintree/runbook/blob/0c0a028dffe88f0bb45ab2afcffe202ae3baa58b/lib/runbook/cli.rb#L52-L66
107
+ * https://github.com/braintree/runbook/blob/0c0a028dffe88f0bb45ab2afcffe202ae3baa58b/lib/runbook/cli.rb#L22-L28
108
+ * https://github.com/braintree/runbook/blob/master/README.md#from-within-your-project
109
+
110
+ #### Blocking Tmux Commands
111
+
112
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
113
+
114
+ Right now tmux commands are started and then immediately return control to the runbook. It would be ideal if a flag could be passed to not return control to the runbook until the tmux command has completed. Additionally, it may be nice to have a block command that waits for one of multiple tmux commands to finish executing. One potential implementation for this is to use `ps`. Another may be to use the `proc` file system. I am not aware of anything built into tmux for this purpose.
115
+
116
+ #### Runbook Logger
117
+
118
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 2**
119
+
120
+ Include a dedicated logger for Runbook that is incorporated into the execution. This can be off by default, but enabled by setting a log file. Additionally, a log-level can be set. Log output should include what is being executed, and what the result is. The log should be compatible with sshkit's log output. Additionally, this should be implemented in a way that all normal output is suppressed and log output is written to stdout. It would be nice if adding logging did not require additional code for each entity and statement execution.
121
+
122
+ #### Mutation Command Skipping
123
+
124
+ **Difficulty: ?**, **Desireability: 2**, **Conceptual Completeness: 3**
125
+
126
+ It would be nice to be able to skip commands that you know will have an effect on your system. The main use case for this is testing. You can test your runbook more thoroughly and have confidence that it will not make changes to your system. It would be difficult if not impossible to determine what commands will have a mutable affect, so this will likely need to be user-determined.
127
+
128
+ One way to accomplish this is through a tags implementation where entities and statements have a set of tags, and their behavior is controlled based on these tags. I have implemented a spike of adding tags to entities. One complication of skipping mutation commands is that it is not easy to determine if future commands rely on that command executing. This could reduce the overall benefit of flagging and skipping mutation commands. The skipping logic could be implemented using Runbook's hooks feature.
129
+
130
+ Skipping mutation commands for the sake of testing may also be accomplished through the use of mocks. This could a testing library or perhapsa mocking solution built out for Runbook. Implementing a mocking pattern could resolve the downstream dependency issue of skipping mutation commands. A mocking solution could also be implemented using Runbook hooks.
131
+
132
+ The mutation command skipping behavior could be controlled via an environment variable. It may be controlled via config or other flag, but this feels too specific to be included in the more general config or run options.
133
+
134
+ #### Revert Section
135
+
136
+ **Difficulty: ?**, **Desireability: 3**, **Conceptual Completeness: 2**
137
+
138
+ It is a best practice to have a plan for reverting any system changes you make. It would be nice to have a way to include this revert plan in with your runbook and simply trigger its execution with a flag. The revert plan may not be straight-forward if the rollout plan did not run to completion, so revert plans may need to assume this to be the case. Also, it does not appear possible to infer what rollback steps would be based on the initial runbook.
139
+
140
+ This functionality could be achieved by adding a new revert entity which responds to a revert flag passed by the user (environment variable, command line config, runner/viewer option). Instead of a separate entity, this could be accomplished with a tagging solution, where a section has a revert tag that designates it should be skipped unless the revert flag is present (and vis-versa for non-revert sections). The skipping logic could be implemented using Runbook hooks.
141
+
142
+ A workaround for this is to simply have a separate revert runbook to execute in the event a rollbock is needed.
143
+
144
+ #### Runbook Versioning
145
+
146
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
147
+
148
+ Right now if the API for the Runbook DSL changes in a backwards incompatible way, the runbook will break when being executed with the new version of Runbook. If we add a version declaration for the runbook, then we can conditionally execute the backwards incompatibile code based on the version.
149
+
150
+ An alternative to including a version declaration would be to only change backwards incompatibilities between major versions of Runbook, and require that users of the new version update all their runbooks to be compatible with the new version. Including a version declaration may make this more explicit and easy to know which versions are an are not compatible. As the version declaration is meant to indicate a DSL backwards incompatibility, this should be detectable by simply compiling the runbooks using the view functionality, or another side-effectless compilation to determine when things break.
151
+
152
+ A version flag can always be added at a later date, and Runbooks without the flag can be considered to be the older version. This could be set to be required for new Runbooks.
153
+
154
+ #### Replace Thor CLI
155
+
156
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 2**
157
+
158
+ It would be nice if Runbook's CLI interface could be customized via the use of plugins. Thor commands and options are specified by executing methods in a specific order in a class, so it is not easily extensible if a plugin wants to customize the CLI. One possible solution is to replace Thor with another CLI solution such as Ruby's build int option parser or GLI. I am not sure what other good solutions out there exist.
159
+
160
+ Another solution could be to extend Thor in a way that additional arguments can "fall through" or perhaps accept variable arguments that get set on runbook config which can be used to modify execution behavior. This solution may be better because it sets a clear boundary between core runbook and plugins.
161
+
162
+ #### Goto Statement
163
+
164
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
165
+
166
+ This statement would provide the same behavior as the jump option at the step menu, allowing you to go to a certain step. Jump may be a better keyword than goto. An existing workaround is to use `ruby_command` and set `start_at` in the metadata. This behavior could be used for repeating steps until a certain criteria is met.
167
+
168
+ #### Test Statement
169
+
170
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
171
+
172
+ This statement would take a command, execute it using SSHKit's test command, and then yield its return value as a boolean that can be conditioned on within a block. The block should have the same context as a ruby_command. A workaround is to use capture combined with echoing `$?` or similar to capture the return value of your command and then condition on the value returned.
173
+
174
+ #### RubyAssert Statement
175
+
176
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
177
+
178
+ This statement would duplicate all of the logic of the current `assert`, but instead of conditioning on whether a command executes successfully, it would condition on whether the block executes successfully. This behavior can currently be accomplished with a `ruby_command`, but it takes a deal of effort to copy the same logic.
179
+
180
+ #### Docker Testing
181
+
182
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
183
+
184
+ Providing integration-level tests that execute features of runbook against docker containers would give us additional test coverage to test features of runbook that use sshkit against remote hosts. Additionally, we could increase our test coverage for our sshkit-sudo functionality.
185
+
186
+ Additionally, it is worth thinking about if any changes to Runbook can help support testing runbooks created by end-users. What is needed to be able to spin up a test environment, execute a runbook, and ensure that the runbook executed successfully?
187
+
188
+ #### Tmux Command Results
189
+
190
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 2**
191
+
192
+ It would be nice to capture output or status codes from an executed tmux command. This could be accomplished by writing the values to a file and then reading them, but it may be nice to have a more streamlined solution.
193
+
194
+ #### Step Dependencies
195
+
196
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 2**
197
+
198
+ In some instances a step may require that a previous step be executed before it can safely execute. It would be nice if these dependencies could be codified, so when a step is skipped, you can still ensure it is executed when executing a step that depends on it. Tracking which steps have been executed would probably want to exist for the life of the execution of the runbook, so surving restarts. In an extreme case, executing a runbook may boil down to a rake-like execution. Perhaps it would be possible to make steps independently executable, and then they could be weaved into rake.
199
+
200
+ #### Update Command Counts
201
+
202
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
203
+
204
+ SSHKit has an incrementing number for each command that is executed. This incrementing number is for the entire runbook which doesn't provide a lot of value. Providing the count per step would be more informative. Additionally, the logic uses the same number for similar commands. For example if you call `ls` twice, it will use the same number each time. It would be nice if this number was more contextual. Probably the best way to accomplish this involves, creating a PR to Airbrussh to allow a context to be set via config. The context would emulate the RakeContext but have different logic for setting the current task and tracking the history of commands. It would probably make sense to make an Abstract Context that defines the required interface for a context, which is `current_task_name`, `register_new_command`, and `position`. This issue has been opened up to track this work: https://github.com/mattbrictson/airbrussh/issues/130
205
+
206
+ Helpful links:
207
+
208
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/configuration.rb#L7
209
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/console_formatter.rb#L19
210
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/rake/context.rb#L47
211
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/command_formatter.rb#L11
212
+
213
+ #### Create Plugin Generator
214
+
215
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 2**
216
+
217
+ Creating a generator for new Runbook plugins would help to make new plugin creation easier. Additionally, it would help to enforce common patterns for new plugins. As no Runbook plugins currently exist, this feature is not a high priority at this point.
218
+
219
+ Some notes on plugins: (1) The switch for enabling a Runbook plugin should be requiring the plugin. (2) If you want to control how certain parts of the plugin are executed, this should be managed using Runbook's configuration.
220
+
221
+ #### Create Run Generator
222
+
223
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
224
+
225
+ This would make creating new runs easier. As creating new runs is a pretty rare occurrence, this feature seems unnecessary at this point, but may be nice for completeness.
226
+
227
+ #### Create View Generator
228
+
229
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
230
+
231
+ This would make creating new views easier. As creating new views is a pretty rare occurrence, this feature seems unnecessary at this point, but may be nice for completeness.
232
+
233
+ #### Outline View Option
234
+
235
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
236
+
237
+ When this option is supplied only entity nodes (book, section, step) would be rendered. This could be nice if you just want an overview of what a runbook does. I have not seen too much demand for this feature, so have not implemented it. It would add an additional option to the view CLI command.
238
+
239
+ #### Guard Runbook
240
+
241
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 2**
242
+
243
+ It could be nice to automatically update Runbook views when a runbook file is modified. This can help identify syntax errors and keep persisted views up to date. It could be helpful to see how your changes are displayed within a Runbook view. This is the main use case I can think of for guard, but perhaps there are others?
244
+
245
+ #### Rake Task Interface
246
+
247
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
248
+
249
+ Provide a rake task interface to Runbook in addition to the ruby invocation interface and the CLI interface. This could be valuable for working with runbooks in a rake-heavy environment. There has not been specific interest in this feature, so it has not gotten prioritized.
250
+
251
+ #### Multiple Commands in Groups
252
+
253
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 3**
254
+
255
+ When specifying to execute a command against multiple servers in groups using the `parallelization` setter, commands are executed in groups for each command, so a single command runs to completion before the next command is executed in groups. It would be nice if there was a way to group a set of commands such that all commands for the first group were executed before commands for the second group were executed. The current workaround for this is to use Ruby to define multiple steps each of which completes against its full set of servers.
256
+
257
+ I do not have an idea of how to cleanly accomplish this.
258
+
259
+ #### Yaml Specification
260
+
261
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 1**
262
+
263
+ Right now the only way to define your Runbook is using Ruby. When the book is evaluated, it gets stored as an intermediate data structure, which happens to be a set of Ruby objects, but the runbook could instead be represented as yaml. The yaml could define your runbook object which is then converted into a Ruby object before executing. I see two main benefits to this. (1) Having a strict data representation of a runbook will help ensure that the representation stays simple and does not get convoluted with complex logic or difficult representations. (2) This could provide an easier method of specification for people uncomfortable writing Ruby.
264
+
265
+ Actually writing a runbook in Yaml, would likely limit the ease of use and feature set for writing a runbook. Specifically, it would require a separate templating language for the yaml (instead of pure Ruby), and ruby_command blocks would need to be eval'ed, there would be no syntax highlighting or other Ruby compilation errors. It may make stack traces more difficult to read or debugging harder.
266
+
267
+ Having a yaml representation of your runbook could aid in additional analysis such as more easily detecting compatibility errors, if all required plugins are present, if there is a semantic vs. syntactic change to the runbook.
268
+
269
+ #### Runbook Web Server
270
+
271
+ **Difficulty: 3**, **Desireability: 3**, **Conceptual Completeness: 3**
272
+
273
+ It might be nice to be able to serve up a local copy of Runbook views so they could be referenced within a web browser. For some this may be more valuable than referencing them on the command line.
274
+
275
+ #### Interactive Runbook Launcher
276
+
277
+ **Difficulty: 2**, **Desireability: 3**, **Conceptual Completeness: 3**
278
+
279
+ It might be nice to provide a cool CLI for listing and navigating the set of runbooks for a project. It could display titles and descriptions and the runbooks could be interactively launched in separate panes. This may be helpful for someone first familiarizing themselves with the runbooks associated with a project. This may depend on a great deal of customization to suite people's workflows so it may not be that valuable. It could be similar to `rake -T` on steroids.
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 5.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 6.0"
6
+
7
+ gemspec path: "../"
@@ -10,5 +10,6 @@ module Runbook::Extensions
10
10
  end
11
11
  end
12
12
 
13
+ Runbook::Entities::Book::DSL.prepend(Steps::DSL)
13
14
  Runbook::Entities::Section::DSL.prepend(Steps::DSL)
14
15
  end
@@ -29,7 +29,11 @@ module Runbook::Generators
29
29
 
30
30
  inside(parent_options[:root]) do
31
31
  test = "--test #{options[:test]}"
32
- run("bundle gem #{_name} #{test} --no-coc --no-mit")
32
+ continue = (
33
+ run("bundle gem #{_name} #{test} --no-coc --no-mit") ||
34
+ options[:pretend]
35
+ )
36
+ exit 1 unless continue
33
37
  end
34
38
  end
35
39
 
@@ -1,5 +1,7 @@
1
1
  module Runbook::Helpers
2
2
  module TmuxHelper
3
+ FILE_PERMISSIONS = 0600
4
+
3
5
  def setup_layout(structure, runbook_title:)
4
6
  _remove_stale_layouts
5
7
  layout_file = _layout_file(_slug(runbook_title))
@@ -11,7 +13,7 @@ module Runbook::Helpers
11
13
  end
12
14
 
13
15
  _setup_layout(structure).tap do |layout_panes|
14
- File.open(layout_file, 'w') do |f|
16
+ File.open(layout_file, 'w', FILE_PERMISSIONS) do |f|
15
17
  f.write(layout_panes.to_yaml)
16
18
  end
17
19
  end
@@ -41,7 +43,7 @@ module Runbook::Helpers
41
43
  end
42
44
 
43
45
  def _slug(title)
44
- title.titleize.gsub(/\s+/, "").underscore.dasherize
46
+ title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
45
47
  end
46
48
 
47
49
  def _all_panes_exist?(stored_layout)
@@ -66,11 +66,12 @@ module Runbook
66
66
 
67
67
  if metadata[:noop]
68
68
  default_msg = default ? " (default: #{default})" : ""
69
- metadata[:toolbox].output("[NOOP] Ask: #{object.prompt} (store in: #{object.into})#{default_msg}")
69
+ echo_msg = object.echo ? "" : " (echo: false)"
70
+ metadata[:toolbox].output("[NOOP] Ask: #{object.prompt} (store in: #{object.into})#{default_msg}#{echo_msg}")
70
71
  return
71
72
  end
72
73
 
73
- result = metadata[:toolbox].ask(object.prompt, default: default)
74
+ result = metadata[:toolbox].ask(object.prompt, default: default, echo: object.echo)
74
75
 
75
76
  target = object.parent.dsl
76
77
  target.singleton_class.class_eval { attr_accessor object.into }
@@ -152,7 +153,7 @@ module Runbook
152
153
  next_index = metadata[:index] + 1
153
154
  parent_items = object.parent.items
154
155
  remaining_items = parent_items.slice!(next_index..-1)
155
- object.parent.dsl.instance_exec(object, metadata, &object.block)
156
+ object.parent.dsl.instance_exec(object, metadata, self, &object.block)
156
157
  parent_items[next_index..-1].each { |item| item.dynamic! }
157
158
  parent_items.push(*remaining_items)
158
159
  end
@@ -1,13 +1,14 @@
1
1
  module Runbook::Runs
2
2
  module SSHKit
3
3
  include Runbook::Run
4
- extend Runbook::Helpers::SSHKitHelper
5
4
 
6
5
  def self.included(base)
7
6
  base.extend(ClassMethods)
8
7
  end
9
8
 
10
9
  module ClassMethods
10
+ include Runbook::Helpers::SSHKitHelper
11
+
11
12
  def runbook__statements__assert(object, metadata)
12
13
  cmd_ssh_config = find_ssh_config(object, :cmd_ssh_config)
13
14
 
@@ -19,18 +20,18 @@ module Runbook::Runs
19
20
  if object.timeout > 0 || object.attempts > 0
20
21
  timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
21
22
  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)
23
+ abort_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, abort..."
24
+ metadata[:toolbox].output(abort_msg)
25
+ if object.abort_statement
26
+ object.abort_statement.parent = object.parent
27
+ object.abort_statement.run(self, metadata.dup)
27
28
  end
28
29
  metadata[:toolbox].output("and exit")
29
30
  end
30
31
  return
31
32
  end
32
33
 
33
- gave_up = false
34
+ should_abort = false
34
35
  test_args = ssh_kit_command(object.cmd, raw: object.cmd_raw)
35
36
  test_options = ssh_kit_command_options(cmd_ssh_config)
36
37
 
@@ -39,12 +40,12 @@ module Runbook::Runs
39
40
  count = object.attempts
40
41
  while !(test(*test_args, test_options))
41
42
  if ((count -= 1) == 0)
42
- gave_up = true
43
+ should_abort = true
43
44
  break
44
45
  end
45
46
 
46
47
  if (object.timeout > 0 && Time.now - time > object.timeout)
47
- gave_up = true
48
+ should_abort = true
48
49
  break
49
50
  end
50
51
 
@@ -52,12 +53,12 @@ module Runbook::Runs
52
53
  end
53
54
  end
54
55
 
55
- if gave_up
56
+ if should_abort
56
57
  error_msg = "Error! Assertion `#{object.cmd}` failed"
57
58
  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)
59
+ if object.abort_statement
60
+ object.abort_statement.parent = object.parent
61
+ object.abort_statement.run(self, metadata.dup)
61
62
  end
62
63
  raise Runbook::Runner::ExecutionError, error_msg
63
64
  end
@@ -1,11 +1,12 @@
1
1
  module Runbook::Statements
2
2
  class Ask < Runbook::Statement
3
- attr_reader :prompt, :into, :default
3
+ attr_reader :prompt, :into, :default, :echo
4
4
 
5
- def initialize(prompt, into:, default: nil)
5
+ def initialize(prompt, into:, default: nil, echo: true)
6
6
  @prompt = prompt
7
7
  @into = into
8
8
  @default = default
9
+ @echo = echo
9
10
  end
10
11
  end
11
12
  end
@@ -2,7 +2,12 @@ module Runbook::Statements
2
2
  class Assert < Runbook::Statement
3
3
  attr_reader :cmd, :cmd_ssh_config, :cmd_raw
4
4
  attr_reader :interval, :timeout, :attempts
5
- attr_reader :timeout_statement
5
+ attr_reader :abort_statement
6
+
7
+ def timeout_statement
8
+ Runbook.deprecator.deprecation_warning(:timeout_statement, :abort_statement)
9
+ @abort_statement
10
+ end
6
11
 
7
12
  def initialize(
8
13
  cmd,
@@ -11,6 +16,7 @@ module Runbook::Statements
11
16
  interval: 1,
12
17
  timeout: 0,
13
18
  attempts: 0,
19
+ abort_statement: nil,
14
20
  timeout_statement: nil
15
21
  )
16
22
  @cmd = cmd
@@ -19,7 +25,10 @@ module Runbook::Statements
19
25
  @interval = interval
20
26
  @timeout = timeout
21
27
  @attempts = attempts
22
- @timeout_statement = timeout_statement
28
+ if timeout_statement
29
+ Runbook.deprecator.deprecation_warning(:timeout_statement, :abort_statement)
30
+ end
31
+ @abort_statement = abort_statement || timeout_statement
23
32
  end
24
33
  end
25
34
  end
@@ -6,8 +6,8 @@ module Runbook
6
6
  @prompt = TTY::Prompt.new
7
7
  end
8
8
 
9
- def ask(msg, default: nil)
10
- prompt.ask(msg, default: default)
9
+ def ask(msg, default: nil, echo: true)
10
+ prompt.ask(msg, default: default, echo: echo)
11
11
  end
12
12
 
13
13
  def expand(msg, choices)
@@ -1,6 +1,7 @@
1
1
  module Runbook::Util
2
2
  module Repo
3
3
  FILE_ID = "data"
4
+ FILE_PERMISSIONS = 0600
4
5
 
5
6
  def self.load(metadata)
6
7
  title = metadata[:book_title]
@@ -13,7 +14,7 @@ module Runbook::Util
13
14
  end
14
15
 
15
16
  def self.save(repo, book_title:)
16
- File.open(_file(book_title), 'w') do |f|
17
+ File.open(_file(book_title), 'w', FILE_PERMISSIONS) do |f|
17
18
  f.write(repo.to_yaml)
18
19
  end
19
20
  end
@@ -27,7 +28,7 @@ module Runbook::Util
27
28
  end
28
29
 
29
30
  def self._slug(title)
30
- title.titleize.gsub(/\s+/, "").underscore.dasherize
31
+ title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
31
32
  end
32
33
 
33
34
  def self.register_save_repo_hook(base)
@@ -1,6 +1,7 @@
1
1
  module Runbook::Util
2
2
  module StoredPose
3
3
  FILE_ID = "current_position"
4
+ FILE_PERMISSIONS = 0600
4
5
 
5
6
  def self.load(metadata)
6
7
  title = metadata[:book_title]
@@ -11,7 +12,7 @@ module Runbook::Util
11
12
  end
12
13
 
13
14
  def self.save(repo, book_title:)
14
- File.open(_file(book_title), 'w') do |f|
15
+ File.open(_file(book_title), 'w', FILE_PERMISSIONS) do |f|
15
16
  f.write(repo.to_yaml)
16
17
  end
17
18
  end
@@ -25,7 +26,7 @@ module Runbook::Util
25
26
  end
26
27
 
27
28
  def self._slug(title)
28
- title.titleize.gsub(/\s+/, "").underscore.dasherize
29
+ title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
29
30
  end
30
31
 
31
32
  def self.register_save_pose_hook(base)
@@ -1,3 +1,3 @@
1
1
  module Runbook
2
- VERSION = "0.14.0"
2
+ VERSION = "0.15.0"
3
3
  end
@@ -31,10 +31,10 @@ module Runbook::Views
31
31
  if object.timeout > 0 || object.attempts > 0
32
32
  timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
33
33
  attempts_msg = object.attempts > 0 ? "#{object.attempts} attempts" : nil
34
- giveup_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, give up..."
35
- output << " #{giveup_msg}\n\n"
36
- if object.timeout_statement
37
- object.timeout_statement.render(self, output, metadata.dup)
34
+ abort_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, abort..."
35
+ output << " #{abort_msg}\n\n"
36
+ if object.abort_statement
37
+ object.abort_statement.render(self, output, metadata.dup)
38
38
  end
39
39
  output << " and exit\n\n"
40
40
  end
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
32
  spec.require_paths = ["lib"]
33
33
 
34
- spec.add_runtime_dependency 'activesupport', '~> 5.0', '>= 5.0.0.1'
34
+ spec.add_runtime_dependency "activesupport", ">= 5.0.1.x", "< 7.0"
35
35
  spec.add_runtime_dependency "method_source", "~> 0.9"
36
36
  spec.add_runtime_dependency "sshkit", "1.16"
37
37
  spec.add_runtime_dependency "sshkit-sudo", "~> 0.1"
@@ -40,6 +40,7 @@ Gem::Specification.new do |spec|
40
40
  spec.add_runtime_dependency "tty-progressbar", "~> 0.14"
41
41
  spec.add_runtime_dependency "tty-prompt", "~> 0.16"
42
42
 
43
+ spec.add_development_dependency "appraisal", "~> 2.2"
43
44
  spec.add_development_dependency "aruba", "~> 0.14"
44
45
  spec.add_development_dependency "bundler", "~> 1.15"
45
46
  spec.add_development_dependency "pry", "~> 0.11"
metadata CHANGED
@@ -1,35 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pblesi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-15 00:00:00.000000000 Z
11
+ date: 2019-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '5.0'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 5.0.0.1
19
+ version: 5.0.1.x
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '5.0'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
- version: 5.0.0.1
29
+ version: 5.0.1.x
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: method_source
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0.16'
131
+ - !ruby/object:Gem::Dependency
132
+ name: appraisal
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.2'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '2.2'
131
145
  - !ruby/object:Gem::Dependency
132
146
  name: aruba
133
147
  requirement: !ruby/object:Gem::Requirement
@@ -227,6 +241,7 @@ files:
227
241
  - ".ruby-gemset"
228
242
  - ".ruby-version"
229
243
  - ".travis.yml"
244
+ - Appraisals
230
245
  - CHANGELOG.md
231
246
  - CODE_OF_CONDUCT.md
232
247
  - Gemfile
@@ -237,6 +252,9 @@ files:
237
252
  - bin/console
238
253
  - bin/setup
239
254
  - exe/runbook
255
+ - gemfiles/.bundle/config
256
+ - gemfiles/activesupport_5.gemfile
257
+ - gemfiles/activesupport_6.gemfile
240
258
  - images/runbook_anatomy_diagram.png
241
259
  - images/runbook_example.gif
242
260
  - images/runbook_execution_modes.png
@@ -333,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
333
351
  - !ruby/object:Gem::Version
334
352
  version: '0'
335
353
  requirements: []
336
- rubygems_version: 3.0.4
354
+ rubygems_version: 3.0.6
337
355
  signing_key:
338
356
  specification_version: 4
339
357
  summary: Write beautiful, executable runbooks for conducting system operations.