careacademy-runbook 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +17 -0
  3. data/.github/workflows/ruby.yml +43 -0
  4. data/.gitignore +16 -0
  5. data/.rspec +2 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/Appraisals +8 -0
  9. data/CHANGELOG.md +143 -0
  10. data/CODE_OF_CONDUCT.md +74 -0
  11. data/Gemfile +6 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +1272 -0
  14. data/Rakefile +12 -0
  15. data/TODO.md +316 -0
  16. data/bin/console +16 -0
  17. data/bin/setup +8 -0
  18. data/dockerfiles/Dockerfile-runbook +18 -0
  19. data/dockerfiles/Dockerfile-sshd +4 -0
  20. data/examples/hooks_runbook.rb +72 -0
  21. data/examples/layout_runbook.rb +26 -0
  22. data/examples/restart_nginx.rb +26 -0
  23. data/examples/simple_runbook.rb +41 -0
  24. data/examples/suppress_capture_output.rb +47 -0
  25. data/exe/runbook +5 -0
  26. data/gemfiles/.bundle/config +2 -0
  27. data/gemfiles/activesupport_5.gemfile +7 -0
  28. data/gemfiles/activesupport_6.gemfile +7 -0
  29. data/images/runbook_anatomy_diagram.png +0 -0
  30. data/images/runbook_example.gif +0 -0
  31. data/images/runbook_execution_modes.png +0 -0
  32. data/lib/hacks/ssh_kit.rb +58 -0
  33. data/lib/runbook/airbrussh_context.rb +25 -0
  34. data/lib/runbook/cli.rb +115 -0
  35. data/lib/runbook/cli_base.rb +38 -0
  36. data/lib/runbook/configuration.rb +120 -0
  37. data/lib/runbook/dsl.rb +21 -0
  38. data/lib/runbook/entities/book.rb +17 -0
  39. data/lib/runbook/entities/section.rb +7 -0
  40. data/lib/runbook/entities/setup.rb +7 -0
  41. data/lib/runbook/entities/step.rb +7 -0
  42. data/lib/runbook/entity.rb +129 -0
  43. data/lib/runbook/errors.rb +7 -0
  44. data/lib/runbook/extensions/add.rb +14 -0
  45. data/lib/runbook/extensions/description.rb +14 -0
  46. data/lib/runbook/extensions/sections.rb +19 -0
  47. data/lib/runbook/extensions/setup.rb +17 -0
  48. data/lib/runbook/extensions/shared_variables.rb +51 -0
  49. data/lib/runbook/extensions/ssh_config.rb +78 -0
  50. data/lib/runbook/extensions/statements.rb +31 -0
  51. data/lib/runbook/extensions/steps.rb +24 -0
  52. data/lib/runbook/extensions/tmux.rb +13 -0
  53. data/lib/runbook/generator.rb +38 -0
  54. data/lib/runbook/generators/base.rb +45 -0
  55. data/lib/runbook/generators/dsl_extension/dsl_extension.rb +29 -0
  56. data/lib/runbook/generators/dsl_extension/templates/dsl_extension.tt +25 -0
  57. data/lib/runbook/generators/generator/generator.rb +43 -0
  58. data/lib/runbook/generators/generator/templates/generator.tt +53 -0
  59. data/lib/runbook/generators/project/project.rb +301 -0
  60. data/lib/runbook/generators/project/templates/Gemfile.tt +6 -0
  61. data/lib/runbook/generators/project/templates/README.md.tt +29 -0
  62. data/lib/runbook/generators/project/templates/Runbookfile.tt +11 -0
  63. data/lib/runbook/generators/project/templates/base_file.rb.tt +8 -0
  64. data/lib/runbook/generators/runbook/runbook.rb +22 -0
  65. data/lib/runbook/generators/runbook/templates/runbook.tt +20 -0
  66. data/lib/runbook/generators/statement/statement.rb +18 -0
  67. data/lib/runbook/generators/statement/templates/statement.tt +34 -0
  68. data/lib/runbook/helpers/format_helper.rb +11 -0
  69. data/lib/runbook/helpers/ssh_kit_helper.rb +143 -0
  70. data/lib/runbook/helpers/tmux_helper.rb +176 -0
  71. data/lib/runbook/hooks.rb +88 -0
  72. data/lib/runbook/initializer.rb +85 -0
  73. data/lib/runbook/node.rb +33 -0
  74. data/lib/runbook/run.rb +290 -0
  75. data/lib/runbook/runner.rb +66 -0
  76. data/lib/runbook/runs/ssh_kit.rb +193 -0
  77. data/lib/runbook/statement.rb +20 -0
  78. data/lib/runbook/statements/ask.rb +12 -0
  79. data/lib/runbook/statements/assert.rb +34 -0
  80. data/lib/runbook/statements/capture.rb +14 -0
  81. data/lib/runbook/statements/capture_all.rb +14 -0
  82. data/lib/runbook/statements/command.rb +11 -0
  83. data/lib/runbook/statements/confirm.rb +10 -0
  84. data/lib/runbook/statements/description.rb +9 -0
  85. data/lib/runbook/statements/download.rb +12 -0
  86. data/lib/runbook/statements/layout.rb +10 -0
  87. data/lib/runbook/statements/note.rb +10 -0
  88. data/lib/runbook/statements/notice.rb +10 -0
  89. data/lib/runbook/statements/ruby_command.rb +9 -0
  90. data/lib/runbook/statements/tmux_command.rb +11 -0
  91. data/lib/runbook/statements/upload.rb +12 -0
  92. data/lib/runbook/statements/wait.rb +10 -0
  93. data/lib/runbook/toolbox.rb +37 -0
  94. data/lib/runbook/util/repo.rb +57 -0
  95. data/lib/runbook/util/runbook.rb +43 -0
  96. data/lib/runbook/util/sticky_hash.rb +26 -0
  97. data/lib/runbook/util/stored_pose.rb +55 -0
  98. data/lib/runbook/version.rb +3 -0
  99. data/lib/runbook/view.rb +24 -0
  100. data/lib/runbook/viewer.rb +24 -0
  101. data/lib/runbook/views/markdown.rb +117 -0
  102. data/lib/runbook.rb +140 -0
  103. data/runbook.gemspec +53 -0
  104. metadata +419 -0
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ spec_all = RSpec::Core::RakeTask.new(:spec_all)
5
+
6
+ spec_task = RSpec::Core::RakeTask.new(:spec)
7
+ spec_task.exclude_pattern = "spec/fullstack/**{,/*/**}/*_spec.rb"
8
+
9
+ spec_fullstack = RSpec::Core::RakeTask.new(:spec_fullstack)
10
+ spec_fullstack.pattern = "spec/fullstack/**{,/*/**}/*_spec.rb"
11
+
12
+ task :default => :spec
data/TODO.md ADDED
@@ -0,0 +1,316 @@
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
+ * [Tmux Command Quote Escaping](#tmux-command-quote-escaping): Eliminate the need to escape single quotes in tmux commands
35
+ * [Always-executed Setup Section](#always-executed-setup-section): Add support for a section that is never skipped
36
+ * [Shortened Tmux Layout Keys](#shortened-tmux-layout-keys): Make tmux layout keys easier
37
+ * [Add Host Aliases for SSH Config](#add-host-aliases-for-ssh-config): Use `host` and `hosts` instead of `server` and `servers`
38
+ * [Capistrano-runbook Gem](#capistrano-runbook-gem): A gem for integrating runbook directly into capistrano
39
+ * [Blocking Tmux Commands](#blocking-tmux-commands): Don't continue execution until the tmux_command completes
40
+ * [Runbook Logger](#runbook-logger): A logger for Runbook
41
+ * [Mutation Command Skipping](#mutation-command-skipping): Allow skipping mutation commands
42
+ * [Revert Section](#revert-section): A section that can be triggered which will revert the changes of the runbook
43
+ * [Better Error Formatting](#better-error-formatting): More cleanly format errors to aid in troubleshooting issues
44
+ * [Runbook Versioning](#runbook-versioning): Specify a version in a runbook to allow for supporting backwards incompatible runbook DSL format changes
45
+ * [Replace Thor CLI](#replace-thor-cli): Replace Thor for Runbook's CLI with something that can be more easily extended and customized
46
+ * [Goto Statement](#goto-statement): A statement for jumping to a specific step
47
+ * [Test Statement](#test-statement): A statement for executing a remote command and conditioning on its success
48
+ * [RubyAssert Statement](#rubyassert-statement): A statement analogous to assert, but executing a block instead of a command
49
+ * [Docker Testing](#docker-testing): Add tests that execute against docker containers
50
+ * [Tmux Command Results](#tmux-command-results): Capture outputs and status codes for tmux commands
51
+ * [Step Dependencies](#step-dependencies): Like rake, allow a step to invoke another step if it has not been executed
52
+ * [Update Command Counts](#update-command-counts): Use step titles for counting commands executed in a step
53
+ * [Create Plugin Generator](#create-plugin-generator): A generator for creating boilerplate for a new Runbook plugin
54
+ * [Create Run Generator](#create-run-generator): A generator for creating boilerplate for a new run such as sshkit
55
+ * [Create View Generator](#create-view-generator): A generator for creating boilerplate for a new view such as markdown, yaml, html
56
+ * [Outline View Option](#outline-view-option): A command-line option to only execute entity nodes
57
+ * [Guard Runbook](#guard-runbook): Automatically update Runbook views when a runbook is saved
58
+ * [Rake Task Interface](#rake-task-interface): Execute and view runbooks with rake tasks
59
+ * [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
60
+ * [Command Line Variables](#command-line-variables): Pass arbitrary variables via the command line
61
+ * [Yaml Specification](#yaml-specification): Define your runbook using yaml instead of Ruby
62
+ * [Runbook Web Server](#runbook-web-server): Automatically serve up Runbook views via the web
63
+ * [Interactive Runbook Launcher](#interactive-runbook-launcher): A CLI launcher to review and execute runbooks
64
+
65
+ ### Feature Details
66
+
67
+ #### Sudo Raw Command Support
68
+
69
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
70
+
71
+ 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.
72
+
73
+ 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).
74
+
75
+ 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.
76
+
77
+ 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.
78
+
79
+ #### Tmux Command Quote Escaping
80
+
81
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
82
+
83
+ Using heredocs and command substitution, it is possible to eliminate the need to escape single quotes passed to tmux_commands. This would go a long way to make this command more intuitive and ensure that the string passed by the user gets executed in the desired pane. This can be accomplished by updating the `send_keys` method to the following:
84
+
85
+ ```ruby
86
+ def send_keys(command, target)
87
+ `
88
+ command=$(cat <<'MAGIC_WORD'
89
+ #{command}
90
+ MAGIC_WORD
91
+ )
92
+ tmux send-keys -t #{target} #{_pager_escape_sequence} "$command" C-m
93
+ `
94
+ end
95
+ ```
96
+
97
+ The two main concerns for this change are (1) is this implementation as widely supported as the current implementation (in terms of shells and their versions) and (2) This would technically be a backwards incompatible change where we would want to provide a deprecation path.
98
+
99
+ Additionally, is it worth pursuing a similar pattern for SSHKit?
100
+
101
+ #### Always-executed Setup Section
102
+
103
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
104
+
105
+ 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.
106
+
107
+ 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.
108
+
109
+ #### Shortened Tmux Layout Keys
110
+
111
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
112
+
113
+ `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.
114
+
115
+ #### Add Host Aliases for SSH Config
116
+
117
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 1**
118
+
119
+ `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.
120
+
121
+ #### Capistrano-runbook Gem
122
+
123
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
124
+
125
+ 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.
126
+
127
+ The implementation for this would be pretty straightforward. It would provide an alternative interface for invoking runbooks (as opposed to invoking via the CLI).
128
+
129
+ Helpful links:
130
+
131
+ * https://github.com/braintree/runbook/blob/0c0a028dffe88f0bb45ab2afcffe202ae3baa58b/lib/runbook/cli.rb#L52-L66
132
+ * https://github.com/braintree/runbook/blob/0c0a028dffe88f0bb45ab2afcffe202ae3baa58b/lib/runbook/cli.rb#L22-L28
133
+ * https://github.com/braintree/runbook/blob/master/README.md#from-within-your-project
134
+
135
+ #### Blocking Tmux Commands
136
+
137
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
138
+
139
+ 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.
140
+
141
+ #### Runbook Logger
142
+
143
+ **Difficulty: 1**, **Desireability: 1**, **Conceptual Completeness: 2**
144
+
145
+ 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.
146
+
147
+ #### Mutation Command Skipping
148
+
149
+ **Difficulty: ?**, **Desireability: 2**, **Conceptual Completeness: 3**
150
+
151
+ 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.
152
+
153
+ 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.
154
+
155
+ Skipping mutation commands for the sake of testing may also be accomplished through the use of mocks. This could be a testing library or perhaps a 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.
156
+
157
+ 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.
158
+
159
+ #### Revert Section
160
+
161
+ **Difficulty: ?**, **Desireability: 3**, **Conceptual Completeness: 2**
162
+
163
+ 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.
164
+
165
+ 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.
166
+
167
+ A workaround for this is to simply have a separate revert runbook to execute in the event a rollbock is needed.
168
+
169
+ #### Better Error Formatting
170
+
171
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 3**
172
+
173
+ Right now interpretting errors and stacktraces is difficult for people new to Runbook or Ruby. Rspec does a very good job of presenting cleanly formatted errors and helpful error messages for common problems. See their [exception presenter](https://github.com/rspec/rspec-core/blob/6c5628fc0ea83ecf51f1a4d5e0eb2036819a0dab/lib/rspec/core/formatters/exception_presenter.rb) as an example. It would be helpful to examine Rspec's practices around error formatting and providing more helpful error messages to see what can be emulated for Runbook.
174
+
175
+ #### Runbook Versioning
176
+
177
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
178
+
179
+ 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.
180
+
181
+ 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.
182
+
183
+ 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.
184
+
185
+ #### Replace Thor CLI
186
+
187
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 2**
188
+
189
+ 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.
190
+
191
+ 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.
192
+
193
+ #### Goto Statement
194
+
195
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
196
+
197
+ 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.
198
+
199
+ #### Test Statement
200
+
201
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
202
+
203
+ 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.
204
+
205
+ #### RubyAssert Statement
206
+
207
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 1**
208
+
209
+ 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.
210
+
211
+ #### Docker Testing
212
+
213
+ **Difficulty: 2**, **Desireability: 1**, **Conceptual Completeness: 2**
214
+
215
+ 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.
216
+
217
+ 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?
218
+
219
+ #### Tmux Command Results
220
+
221
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 2**
222
+
223
+ 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.
224
+
225
+ #### Step Dependencies
226
+
227
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 2**
228
+
229
+ 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 surviving 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.
230
+
231
+ #### Update Command Counts
232
+
233
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
234
+
235
+ 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
236
+
237
+ Helpful links:
238
+
239
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/configuration.rb#L7
240
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/console_formatter.rb#L19
241
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/rake/context.rb#L47
242
+ * https://github.com/mattbrictson/airbrussh/blob/918c072254c8bf78982bb6a5e384cedc8e361836/lib/airbrussh/command_formatter.rb#L11
243
+
244
+ #### Create Plugin Generator
245
+
246
+ **Difficulty: 1**, **Desireability: 2**, **Conceptual Completeness: 2**
247
+
248
+ 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.
249
+
250
+ 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.
251
+
252
+ #### Create Run Generator
253
+
254
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
255
+
256
+ 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.
257
+
258
+ #### Create View Generator
259
+
260
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
261
+
262
+ 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.
263
+
264
+ #### Outline View Option
265
+
266
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
267
+
268
+ 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.
269
+
270
+ #### Guard Runbook
271
+
272
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 2**
273
+
274
+ 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?
275
+
276
+ #### Rake Task Interface
277
+
278
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 1**
279
+
280
+ 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.
281
+
282
+ #### Multiple Commands in Groups
283
+
284
+ **Difficulty: 3**, **Desireability: 2**, **Conceptual Completeness: 3**
285
+
286
+ 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.
287
+
288
+ I do not have an idea of how to cleanly accomplish this.
289
+
290
+ #### Command Line Variables
291
+
292
+ **Difficulty: 1**, **Desireability: 3**, **Conceptual Completeness: 2**
293
+
294
+ Add a `vars` command-line option for runtime variables passed through the command line. These arbitrary command-line vars would be stored in the metadata and accessible at runtime (for example in a `ruby_command` block). These can be useful for modifying the runbook's behavior within hooks in conjunction with tags and labels. This functionality already exists however with the use of environment variables. Environment variables are also available at compile time, where as these command line variables are not. In order to make the command line variables available at compile time, they would have to be set in a global context. It is not clear if these values would persist between runs. It may be best practice to save them and overwrite them if re-defined on the command line, but it is not clear if this is intuitive.
295
+
296
+ #### Yaml Specification
297
+
298
+ **Difficulty: 2**, **Desireability: 2**, **Conceptual Completeness: 1**
299
+
300
+ 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.
301
+
302
+ 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.
303
+
304
+ 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.
305
+
306
+ #### Runbook Web Server
307
+
308
+ **Difficulty: 3**, **Desireability: 3**, **Conceptual Completeness: 3**
309
+
310
+ 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.
311
+
312
+ #### Interactive Runbook Launcher
313
+
314
+ **Difficulty: 2**, **Desireability: 3**, **Conceptual Completeness: 3**
315
+
316
+ 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.
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "runbook"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ Runbook::Configuration.load_config
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ require "pry"
13
+ Pry.start
14
+
15
+ # require "irb"
16
+ # IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,18 @@
1
+ FROM ruby:3.0
2
+ RUN apt-get update -qq && apt-get install -y build-essential tmux
3
+
4
+ RUN mkdir /runbook
5
+ WORKDIR /runbook
6
+
7
+ RUN mkdir /runbook/bin
8
+ ADD bin/setup /runbook/bin/setup
9
+
10
+ ADD runbook.gemspec /runbook/runbook.gemspec
11
+ ADD Gemfile /runbook/Gemfile
12
+
13
+ RUN mkdir -p /runbook/lib/runbook
14
+ ADD lib/runbook/version.rb /runbook/lib/runbook/version.rb
15
+
16
+ RUN bin/setup
17
+
18
+ ADD . /runbook
@@ -0,0 +1,4 @@
1
+ FROM panubo/sshd:1.0.3
2
+
3
+ RUN apk update && \
4
+ apk add sudo
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+ require "runbook"
3
+
4
+ Runbook::Runs::SSHKit.register_hook(
5
+ :output_before_hook,
6
+ :before,
7
+ Object
8
+ ) do |object, metadata|
9
+ location = "#{object.class}: #{metadata[:position]}"
10
+ metadata[:toolbox].output " [Before: #{location}]\n"
11
+ end
12
+
13
+ Runbook::Runs::SSHKit.register_hook(
14
+ :output_around_hook,
15
+ :around,
16
+ Object
17
+ ) do |object, metadata, block|
18
+ location = "#{object.class}: #{metadata[:position]}"
19
+ metadata[:toolbox].output " [Around_before: #{location}]\n"
20
+ block.call(object, metadata)
21
+ metadata[:toolbox].output " [Around_after: #{location}]\n"
22
+ end
23
+
24
+ Runbook::Runs::SSHKit.register_hook(
25
+ :output_after_hook,
26
+ :after,
27
+ Object
28
+ ) do |object, metadata|
29
+ location = "#{object.class}: #{metadata[:position]}"
30
+ metadata[:toolbox].output " [After: #{location}]\n"
31
+ end
32
+
33
+ runbook = Runbook.book "Example Hooks Runbook" do
34
+ description <<-DESC
35
+ This is a runbook for playing with runbook hooks
36
+ DESC
37
+
38
+ layout [[
39
+ :runbook,
40
+ :bottom,
41
+ ]]
42
+
43
+ section "Hook" do
44
+ step do
45
+ note "Looky, Looky, I got a hooky"
46
+ end
47
+
48
+ step
49
+ end
50
+
51
+ section "Fishing Hooks" do
52
+ step do
53
+ note "Hooked, line, and sinker"
54
+ end
55
+
56
+ step
57
+ end
58
+
59
+ section "Hooked on Phonics" do
60
+ step do
61
+ note "Huked on Phoonics reely werx 4 mee"
62
+ end
63
+
64
+ step
65
+ end
66
+ end
67
+
68
+ if __FILE__ == $0
69
+ Runbook::Runner.new(runbook).run
70
+ else
71
+ runbook
72
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require "runbook"
3
+
4
+ runbook = Runbook.book "Example Layout Book" do
5
+ description <<-DESC
6
+ This is a runbook for playing with the layout statement
7
+ DESC
8
+
9
+ layout [[
10
+ [:runbook, :deploy],
11
+ [:monitor_1, :monitor_2, :monitor_3],
12
+ ]]
13
+
14
+ section "Layout Testing" do
15
+ step do
16
+ tmux_command "echo 'Layouts Rock!'", :deploy
17
+ note "Layouts are cool!"
18
+ end
19
+ end
20
+ end
21
+
22
+ if __FILE__ == $0
23
+ Runbook::Runner.new(runbook).run
24
+ else
25
+ runbook
26
+ end
@@ -0,0 +1,26 @@
1
+ Runbook.book "Restart Nginx" do
2
+ description <<-DESC
3
+ This is a simple runbook to restart nginx and verify
4
+ it starts successfully
5
+ DESC
6
+
7
+ section "Restart Nginx" do
8
+ server "app01.prod"
9
+
10
+ step "Stop Nginx" do
11
+ note "Stopping Nginx..."
12
+ command "service nginx stop"
13
+ assert %q{service nginx status | grep "not running"}
14
+ end
15
+
16
+ step { wait 5 }
17
+
18
+ step "Start Nginx" do
19
+ note "Starting Nginx..."
20
+ command "service nginx start"
21
+ assert %q{service nginx status | grep "is running"}
22
+ confirm "Nginx is taking traffic?"
23
+ notice "Make sure to report why you restarted nginx"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ require "runbook"
3
+
4
+ runbook = Runbook.book "Simple Book" do
5
+ description <<-DESC
6
+ This is a simple runbook that does stuff
7
+ DESC
8
+
9
+ section "Parent Section" do
10
+ section "First Section" do
11
+ step "Step 1" do
12
+ parallelization({strategy: :sequence})
13
+ servers "pblesi@server01.stg", "pblesi@server02.stg"
14
+ user "root"
15
+ path "/home/pblesi"
16
+ env rails_env: "development"
17
+ umask "077"
18
+
19
+ note "I like cheese"
20
+ note "I also like carrots"
21
+ command "echo I love cheese"
22
+ command "whoami"
23
+ end
24
+ end
25
+
26
+ section "Second Section" do
27
+ step "Step 1" do
28
+ notice "Some cheese is actually yellow plastic"
29
+ ruby_command do |rb_cmd, metadata|
30
+ metadata[:toolbox].output("I like cheese whiz!")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ if __FILE__ == $0
38
+ Runbook::Runner.new(runbook).run
39
+ else
40
+ runbook
41
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require "runbook"
3
+
4
+ class QuietToolbox < Runbook::Toolbox
5
+ def output(msg); end
6
+ def warn(msg); end
7
+ def error(msg); end
8
+ end
9
+
10
+ Runbook::Runs::SSHKit.register_hook(
11
+ :set_suppress_statement_output_hook,
12
+ :around,
13
+ Runbook::Statement
14
+ ) do |object, metadata, block|
15
+ parent_entity = object.parent_entity
16
+ toolbox = metadata[:toolbox]
17
+ formatter = Runbook.config.ssh_kit.output
18
+ if parent_entity.tags.include?(:suppress_statement_output) || parent_entity.labels[:suppress_statement_output]
19
+ Runbook.config.ssh_kit.output = Runbook.config.ssh_kit.use_format(:blackhole)
20
+ metadata[:toolbox] = QuietToolbox.new
21
+ end
22
+ block.call(object, metadata)
23
+ Runbook.config.ssh_kit.output = formatter
24
+ metadata[:toolbox] = toolbox
25
+ end
26
+
27
+ runbook = Runbook.book "Capture Output Suppression" do
28
+ description <<-DESC
29
+ This is a runbook that suppresses capture output
30
+ DESC
31
+
32
+ section "Demo Output Capture" do
33
+ step "Capturing output", :suppress_statement_output do
34
+ capture "echo hi", into: :echo_output
35
+ end
36
+
37
+ step "Printing output" do
38
+ ruby_command { notice echo_output }
39
+ end
40
+ end
41
+ end
42
+
43
+ if __FILE__ == $0
44
+ Runbook::Runner.new(runbook).run
45
+ else
46
+ runbook
47
+ end
data/exe/runbook ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'runbook/cli'
4
+
5
+ Runbook::CLI.start
@@ -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: "../"
Binary file
Binary file
Binary file