runbook 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/CHANGELOG.md +46 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +6 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +999 -0
  12. data/Rakefile +6 -0
  13. data/TODO.md +38 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +8 -0
  16. data/exe/runbook +5 -0
  17. data/images/runbook_anatomy_diagram.png +0 -0
  18. data/images/runbook_example.gif +0 -0
  19. data/images/runbook_execution_modes.png +0 -0
  20. data/lib/hacks/ssh_kit.rb +58 -0
  21. data/lib/runbook/cli.rb +90 -0
  22. data/lib/runbook/configuration.rb +110 -0
  23. data/lib/runbook/dsl.rb +21 -0
  24. data/lib/runbook/entities/book.rb +17 -0
  25. data/lib/runbook/entities/section.rb +7 -0
  26. data/lib/runbook/entities/step.rb +7 -0
  27. data/lib/runbook/entity.rb +127 -0
  28. data/lib/runbook/errors.rb +7 -0
  29. data/lib/runbook/extensions/add.rb +13 -0
  30. data/lib/runbook/extensions/description.rb +14 -0
  31. data/lib/runbook/extensions/sections.rb +15 -0
  32. data/lib/runbook/extensions/shared_variables.rb +51 -0
  33. data/lib/runbook/extensions/ssh_config.rb +76 -0
  34. data/lib/runbook/extensions/statements.rb +26 -0
  35. data/lib/runbook/extensions/steps.rb +14 -0
  36. data/lib/runbook/extensions/tmux.rb +13 -0
  37. data/lib/runbook/helpers/format_helper.rb +11 -0
  38. data/lib/runbook/helpers/ssh_kit_helper.rb +143 -0
  39. data/lib/runbook/helpers/tmux_helper.rb +174 -0
  40. data/lib/runbook/hooks.rb +88 -0
  41. data/lib/runbook/node.rb +23 -0
  42. data/lib/runbook/run.rb +283 -0
  43. data/lib/runbook/runner.rb +64 -0
  44. data/lib/runbook/runs/ssh_kit.rb +186 -0
  45. data/lib/runbook/statement.rb +22 -0
  46. data/lib/runbook/statements/ask.rb +11 -0
  47. data/lib/runbook/statements/assert.rb +25 -0
  48. data/lib/runbook/statements/capture.rb +14 -0
  49. data/lib/runbook/statements/capture_all.rb +14 -0
  50. data/lib/runbook/statements/command.rb +11 -0
  51. data/lib/runbook/statements/confirm.rb +10 -0
  52. data/lib/runbook/statements/description.rb +9 -0
  53. data/lib/runbook/statements/download.rb +12 -0
  54. data/lib/runbook/statements/layout.rb +10 -0
  55. data/lib/runbook/statements/note.rb +10 -0
  56. data/lib/runbook/statements/notice.rb +10 -0
  57. data/lib/runbook/statements/ruby_command.rb +9 -0
  58. data/lib/runbook/statements/tmux_command.rb +11 -0
  59. data/lib/runbook/statements/upload.rb +12 -0
  60. data/lib/runbook/statements/wait.rb +10 -0
  61. data/lib/runbook/toolbox.rb +43 -0
  62. data/lib/runbook/util/repo.rb +56 -0
  63. data/lib/runbook/util/runbook.rb +25 -0
  64. data/lib/runbook/util/sticky_hash.rb +26 -0
  65. data/lib/runbook/util/stored_pose.rb +54 -0
  66. data/lib/runbook/version.rb +3 -0
  67. data/lib/runbook/view.rb +24 -0
  68. data/lib/runbook/viewer.rb +24 -0
  69. data/lib/runbook/views/markdown.rb +109 -0
  70. data/lib/runbook.rb +110 -0
  71. data/runbook.gemspec +48 -0
  72. data/samples/hooks_runbook.rb +72 -0
  73. data/samples/layout_runbook.rb +26 -0
  74. data/samples/restart_nginx.rb +26 -0
  75. data/samples/simple_runbook.rb +41 -0
  76. metadata +324 -0
@@ -0,0 +1,10 @@
1
+ module Runbook::Statements
2
+ class Notice < Runbook::Statement
3
+ attr_reader :msg
4
+
5
+ def initialize(msg)
6
+ @msg = msg
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,9 @@
1
+ module Runbook::Statements
2
+ class RubyCommand < Runbook::Statement
3
+ attr_reader :block
4
+
5
+ def initialize(&block)
6
+ @block = block
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Runbook::Statements
2
+ class TmuxCommand < Runbook::Statement
3
+ attr_reader :cmd, :pane
4
+
5
+ def initialize(cmd, pane)
6
+ @cmd = cmd
7
+ @pane = pane
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,12 @@
1
+ module Runbook::Statements
2
+ class Upload < Runbook::Statement
3
+ attr_reader :from, :to, :options, :ssh_config
4
+
5
+ def initialize(from, to:, ssh_config: nil, options: {})
6
+ @from = from
7
+ @to = to
8
+ @ssh_config = ssh_config
9
+ @options = options
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Runbook::Statements
2
+ class Wait < Runbook::Statement
3
+ attr_reader :time
4
+
5
+ def initialize(time)
6
+ @time = time
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,43 @@
1
+ module Runbook
2
+ class Toolbox
3
+ attr_reader :prompt
4
+
5
+ def initialize
6
+ @prompt = TTY::Prompt.new
7
+ end
8
+
9
+ def ask(msg, default: nil)
10
+ prompt.ask(msg, default: default)
11
+ end
12
+
13
+ def expand(msg, choices)
14
+ prompt.expand(msg, choices)
15
+ end
16
+
17
+ def yes?(msg)
18
+ begin
19
+ prompt.yes?(msg)
20
+ rescue TTY::Prompt::ConversionError
21
+ err_msg = "Unknown input: Please type 'y' or 'n'."
22
+ warn(err_msg)
23
+ retry
24
+ end
25
+ end
26
+
27
+ def output(msg)
28
+ prompt.say(msg)
29
+ end
30
+
31
+ def warn(msg)
32
+ prompt.warn(msg)
33
+ end
34
+
35
+ def error(msg)
36
+ prompt.error(msg)
37
+ end
38
+
39
+ def exit(return_value)
40
+ super(return_value)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,56 @@
1
+ module Runbook::Util
2
+ module Repo
3
+ FILE_ID = "data"
4
+
5
+ def self.load(metadata)
6
+ title = metadata[:book_title]
7
+ file = _file(title)
8
+ if File.exists?(file)
9
+ msg = "Repo file #{file} detected. Loading previous state..."
10
+ metadata[:toolbox].output(msg)
11
+ metadata[:repo] = ::YAML::load_file(file)
12
+ end
13
+ end
14
+
15
+ def self.save(repo, book_title:)
16
+ File.open(_file(book_title), 'w') do |f|
17
+ f.write(repo.to_yaml)
18
+ end
19
+ end
20
+
21
+ def self.delete(title)
22
+ FileUtils.rm_f(_file(title))
23
+ end
24
+
25
+ def self._file(book_title)
26
+ "#{Dir.tmpdir}/runbook_#{FILE_ID}_#{ENV["USER"]}_#{_slug(book_title)}.yml"
27
+ end
28
+
29
+ def self._slug(title)
30
+ title.titleize.gsub(/\s+/, "").underscore.dasherize
31
+ end
32
+
33
+ def self.register_save_repo_hook(base)
34
+ base.register_hook(
35
+ :save_repo_hook,
36
+ :after,
37
+ Object,
38
+ ) do |object, metadata|
39
+ repo = metadata[:repo]
40
+ title = metadata[:book_title]
41
+ Runbook::Util::Repo.save(repo, book_title: title)
42
+ end
43
+ end
44
+
45
+ def self.register_delete_stored_repo_hook(base)
46
+ base.register_hook(
47
+ :delete_stored_repo_hook,
48
+ :after,
49
+ Runbook::Entities::Book,
50
+ ) do |object, metadata|
51
+ title = metadata[:book_title]
52
+ Runbook::Util::Repo.delete(title)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ module Runbook
2
+ def self.entities
3
+ _child_classes(Runbook::Entities)
4
+ end
5
+
6
+ def self.statements
7
+ _child_classes(Runbook::Statements)
8
+ end
9
+
10
+ def self.runs
11
+ _child_modules(Runbook::Runs)
12
+ end
13
+
14
+ def self._child_classes(mod)
15
+ mod.constants.map { |const|
16
+ "#{mod.to_s}::#{const}".constantize
17
+ }.select { |const| const.is_a?(Class) }
18
+ end
19
+
20
+ def self._child_modules(mod)
21
+ mod.constants.map { |const|
22
+ "#{mod.to_s}::#{const}".constantize
23
+ }.select { |const| const.is_a?(Module) }
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module Runbook::Util
2
+ class StickyHash < Hash
3
+ def [](key)
4
+ value = super
5
+ value.is_a?(Glue) ? value.val : value
6
+ end
7
+
8
+ def []=(key, value)
9
+ assoc = self.assoc(key)
10
+ if assoc.nil? || ! assoc[1].is_a?(Glue)
11
+ super
12
+ else
13
+ assoc[1].val = value
14
+ end
15
+ end
16
+ end
17
+
18
+ class Glue
19
+ attr_accessor :val
20
+
21
+ def initialize(val)
22
+ @val = val
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,54 @@
1
+ module Runbook::Util
2
+ module StoredPose
3
+ FILE_ID = "current_position"
4
+
5
+ def self.load(metadata)
6
+ title = metadata[:book_title]
7
+ file = _file(title)
8
+ if File.exists?(file)
9
+ ::YAML::load_file(file)
10
+ end
11
+ end
12
+
13
+ def self.save(repo, book_title:)
14
+ File.open(_file(book_title), 'w') do |f|
15
+ f.write(repo.to_yaml)
16
+ end
17
+ end
18
+
19
+ def self.delete(title)
20
+ FileUtils.rm_f(_file(title))
21
+ end
22
+
23
+ def self._file(book_title)
24
+ "#{Dir.tmpdir}/runbook_#{FILE_ID}_#{ENV["USER"]}_#{_slug(book_title)}.yml"
25
+ end
26
+
27
+ def self._slug(title)
28
+ title.titleize.gsub(/\s+/, "").underscore.dasherize
29
+ end
30
+
31
+ def self.register_save_pose_hook(base)
32
+ base.register_hook(
33
+ :save_pose_hook,
34
+ :before,
35
+ Object,
36
+ ) do |object, metadata|
37
+ position = metadata[:position]
38
+ title = metadata[:book_title]
39
+ Runbook::Util::StoredPose.save(position, book_title: title)
40
+ end
41
+ end
42
+
43
+ def self.register_delete_stored_pose_hook(base)
44
+ base.register_hook(
45
+ :delete_stored_pose_hook,
46
+ :after,
47
+ Runbook::Entities::Book,
48
+ ) do |object, metadata|
49
+ title = metadata[:book_title]
50
+ Runbook::Util::StoredPose.delete(title)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module Runbook
2
+ VERSION = "0.12.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ module Runbook
2
+ module View
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ include Runbook::Hooks
9
+
10
+ def render(object, output, metadata)
11
+ method = _method_name(object)
12
+ if respond_to?(method)
13
+ send(method, object, output, metadata)
14
+ else
15
+ $stderr.puts("WARNING! No render rule for #{object.class} (#{_method_name(object)}) in #{self.to_s}")
16
+ end
17
+ end
18
+
19
+ def _method_name(object)
20
+ object.class.to_s.underscore.gsub("/", "__")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Runbook
2
+ class Viewer
3
+ attr_reader :book
4
+
5
+ def initialize(book)
6
+ @book = book
7
+ end
8
+
9
+ def generate(view: :markdown)
10
+ view = "Runbook::Views::#{view.to_s.camelize}".constantize
11
+ metadata = Util::StickyHash.new.
12
+ merge(Runbook::Entities::Book.initial_render_metadata).
13
+ merge(additional_metadata)
14
+
15
+ StringIO.new.tap do |output|
16
+ book.render(view, output, metadata)
17
+ end.string
18
+ end
19
+
20
+ def additional_metadata
21
+ {}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,109 @@
1
+ module Runbook::Views
2
+ module Markdown
3
+ include Runbook::View
4
+ extend Runbook::Helpers::FormatHelper
5
+ extend Runbook::Helpers::SSHKitHelper
6
+
7
+ def self.runbook__entities__book(object, output, metadata)
8
+ output << "# #{object.title}\n\n"
9
+ end
10
+
11
+ def self.runbook__entities__section(object, output, metadata)
12
+ heading = "#"*metadata[:depth]
13
+ output << "#{heading} #{metadata[:index]+1}. #{object.title}\n\n"
14
+ end
15
+
16
+ def self.runbook__entities__step(object, output, metadata)
17
+ output << "#{metadata[:index]+1}. [] #{object.title}\n\n"
18
+
19
+ ssh_config = find_ssh_config(object)
20
+ ssh_config_output = render_ssh_config_output(ssh_config)
21
+ output << "#{ssh_config_output}\n" unless ssh_config_output.empty?
22
+ end
23
+
24
+ def self.runbook__statements__ask(object, output, metadata)
25
+ default_msg = object.default ? " (default: #{object.default})" : ""
26
+ output << " #{object.prompt} into #{object.into}#{default_msg}\n\n"
27
+ end
28
+
29
+ def self.runbook__statements__assert(object, output, metadata)
30
+ output << " run: `#{object.cmd}` every #{object.interval} seconds until it returns 0\n\n"
31
+ if object.timeout > 0 || object.attempts > 0
32
+ timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
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)
38
+ end
39
+ output << " and exit\n\n"
40
+ end
41
+ end
42
+
43
+ def self.runbook__statements__capture(object, output, metadata)
44
+ output << " capture: `#{object.cmd}` into #{object.into}\n\n"
45
+ end
46
+
47
+ def self.runbook__statements__capture_all(object, output, metadata)
48
+ output << " capture_all: `#{object.cmd}` into #{object.into}\n\n"
49
+ end
50
+
51
+ def self.runbook__statements__command(object, output, metadata)
52
+ output << " run: `#{object.cmd}`\n\n"
53
+ end
54
+
55
+ def self.runbook__statements__confirm(object, output, metadata)
56
+ output << " confirm: #{object.prompt}\n\n"
57
+ end
58
+
59
+ def self.runbook__statements__description(object, output, metadata)
60
+ output << "#{object.msg}\n"
61
+ end
62
+
63
+ def self.runbook__statements__download(object, output, metadata)
64
+ options = object.options
65
+ to = " to #{object.to}" if object.to
66
+ opts = " with options #{options}" unless options == {}
67
+ output << " download: #{object.from}#{to}#{opts}\n\n"
68
+ end
69
+
70
+ def self.runbook__statements__layout(object, output, metadata)
71
+ output << "layout:\n"
72
+ output << "#{object.structure.inspect}\n\n"
73
+ end
74
+
75
+ def self.runbook__statements__note(object, output, metadata)
76
+ output << " #{object.msg}\n\n"
77
+ end
78
+
79
+ def self.runbook__statements__notice(object, output, metadata)
80
+ output << " **#{object.msg}**\n\n"
81
+ end
82
+
83
+ def self.runbook__statements__ruby_command(object, output, metadata)
84
+ output << " run:\n"
85
+ output << " ```ruby\n"
86
+ begin
87
+ output << "#{deindent(object.block.source, padding: 3)}\n"
88
+ rescue ::MethodSource::SourceNotFoundError => e
89
+ output << " Unable to retrieve source code\n"
90
+ end
91
+ output << " ```\n\n"
92
+ end
93
+
94
+ def self.runbook__statements__tmux_command(object, output, metadata)
95
+ output << " run: `#{object.cmd}` in pane #{object.pane}\n\n"
96
+ end
97
+
98
+ def self.runbook__statements__upload(object, output, metadata)
99
+ options = object.options
100
+ to = " to #{object.to}" if object.to
101
+ opts = " with options #{options}" unless options == {}
102
+ output << " upload: #{object.from}#{to}#{opts}\n\n"
103
+ end
104
+
105
+ def self.runbook__statements__wait(object, output, metadata)
106
+ output << " wait: #{object.time} seconds\n\n"
107
+ end
108
+ end
109
+ end
data/lib/runbook.rb ADDED
@@ -0,0 +1,110 @@
1
+ require "tmpdir"
2
+ require "yaml"
3
+ require "thread"
4
+
5
+ require "active_support/inflector"
6
+ require "method_source"
7
+ require "pastel"
8
+ require "sshkit"
9
+ require "sshkit/sudo"
10
+ require "airbrussh"
11
+ require "tty-progressbar"
12
+ require "tty-prompt"
13
+
14
+ require "runbook/configuration"
15
+
16
+ require "hacks/ssh_kit"
17
+
18
+ require "runbook/dsl"
19
+ require "runbook/errors"
20
+ require "runbook/hooks"
21
+
22
+ require "runbook/util/repo"
23
+ require "runbook/util/runbook"
24
+ require "runbook/util/sticky_hash"
25
+ require "runbook/util/stored_pose"
26
+
27
+ require "runbook/node"
28
+
29
+ require "runbook/entity"
30
+ require "runbook/entities/book"
31
+ require "runbook/entities/section"
32
+ require "runbook/entities/step"
33
+
34
+ require "runbook/statement"
35
+ require "runbook/statements/ask"
36
+ require "runbook/statements/assert"
37
+ require "runbook/statements/capture"
38
+ require "runbook/statements/capture_all"
39
+ require "runbook/statements/command"
40
+ require "runbook/statements/confirm"
41
+ require "runbook/statements/description"
42
+ require "runbook/statements/download"
43
+ require "runbook/statements/layout"
44
+ require "runbook/statements/note"
45
+ require "runbook/statements/notice"
46
+ require "runbook/statements/ruby_command"
47
+ require "runbook/statements/tmux_command"
48
+ require "runbook/statements/upload"
49
+ require "runbook/statements/wait"
50
+
51
+ require "runbook/helpers/format_helper"
52
+ require "runbook/helpers/ssh_kit_helper"
53
+ require "runbook/helpers/tmux_helper"
54
+
55
+ require "runbook/runner"
56
+ require "runbook/run"
57
+ require "runbook/runs/ssh_kit"
58
+
59
+ require "runbook/toolbox"
60
+
61
+ require "runbook/viewer"
62
+ require "runbook/view"
63
+ require "runbook/views/markdown"
64
+
65
+ require "runbook/extensions/add"
66
+ require "runbook/extensions/description"
67
+ require "runbook/extensions/shared_variables"
68
+ require "runbook/extensions/sections"
69
+ require "runbook/extensions/ssh_config"
70
+ require "runbook/extensions/statements"
71
+ require "runbook/extensions/steps"
72
+ require "runbook/extensions/tmux"
73
+
74
+ require "runbook/version"
75
+
76
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
77
+ inflect.acronym 'SSH'
78
+ end
79
+
80
+ module Runbook
81
+ def self.book(title, &block)
82
+ Configuration.load_config
83
+ Entities::Book.new(title).tap do |book|
84
+ book.dsl.instance_eval(&block)
85
+ register(book)
86
+ end
87
+ end
88
+
89
+ def self.section(title, &block)
90
+ Configuration.load_config
91
+ Entities::Section.new(title).tap do |section|
92
+ section.dsl.instance_eval(&block)
93
+ end
94
+ end
95
+
96
+ def self.step(title=nil, &block)
97
+ Configuration.load_config
98
+ Entities::Step.new(title).tap do |step|
99
+ step.dsl.instance_eval(&block) if block
100
+ end
101
+ end
102
+
103
+ def self.register(book)
104
+ books << book
105
+ end
106
+
107
+ def self.books
108
+ @books ||= []
109
+ end
110
+ end
data/runbook.gemspec ADDED
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "runbook/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "runbook"
8
+ spec.version = Runbook::VERSION
9
+ spec.authors = ["pblesi"]
10
+ spec.email = ["code@getbraintree.com"]
11
+
12
+ spec.summary = %q{Write beautiful, executable runbooks for conducting production operations.}
13
+ spec.description = %q{Runbook provides a DSL for specifying production operations. This DSL is used to generate formatted runbooks as well as interactive runbooks to be executed on the command line.}
14
+ spec.homepage = "https://github.com/braintree/runbook/"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_runtime_dependency 'activesupport', '~> 5.0', '>= 5.0.0.1'
34
+ spec.add_runtime_dependency "method_source", "~> 0.9.0"
35
+ spec.add_runtime_dependency "sshkit", "1.16"
36
+ spec.add_runtime_dependency "sshkit-sudo", "~> 0.1.0"
37
+ spec.add_runtime_dependency "airbrussh", "~> 1.3"
38
+ spec.add_runtime_dependency "thor", "~> 0.20.0"
39
+ spec.add_runtime_dependency "tty-progressbar", "~> 0.14.0"
40
+ spec.add_runtime_dependency "tty-prompt", "~> 0.16.0"
41
+
42
+ spec.add_development_dependency "aruba", "~> 0.14.5"
43
+ spec.add_development_dependency "bundler", "~> 1.15"
44
+ spec.add_development_dependency "pry", "~> 0.11.3"
45
+ spec.add_development_dependency "pry-byebug", "~> 3.6"
46
+ spec.add_development_dependency "rake", "~> 10.0"
47
+ spec.add_development_dependency "rspec", "~> 3.0"
48
+ end
@@ -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