braintree / runbook

A framework for gradual system automation
MIT License
730 stars 43 forks source link

Allow 'ask' to be run at "compile" time #16

Open mpalmer opened 4 years ago

mpalmer commented 4 years ago

For semi-automated runbooks, it is useful to be able to collect all the necessary information that is needed by a runbook up-front, before the steps start executing. In the simple case, you can just ask all your questions in the first step, but if you're aggregating multiple sections or runbooks together into one mega runbook o' doom, and each of the components has questions of its own, you end up having to come back to the run all the time to answer more questions. If I could answer all the questions at once, and then wander off to get a cuppa while the runbook does its thing, I would be a much happier (and well-caffeinated) person.

pblesi commented 4 years ago

I like this idea, but have struggled to find a good implementation. If you can think of a good way to accomplish this, I would be interested. A couple of thoughts:

1) Typically for gathering input at compile time, I suggest relying on environment variables.

Runbook.book "Bootstrap Servers" do
  step "Setup" do
    ruby_command do
      @rails_env ||= ENV["RAILS_ENV"]
      ask "Environment?", into: :rails_env, default: "staging" unless @rails_env
    end
  end
end
$ RAILS_ENV=production bundle exec runbook exec my_runbook.rb 

Alternatively, TTY::Prompt is a dependency of Runbook, so it could be used to collect user input before the runbook is evaluated. The main concern with this is it prevents a transition to full autonomy for the runbook. If this is unlikely to be necessary for your particular use case, then this approach may be a good route to go. Another downside of this is that you will be re-prompted every time you execute the runbook, even if you encounter an error. It also will not remember previous inputs.

2) Switching to some OOP might be useful. You could do something like the following:

# runbooks/restart_cluster.rb
require 'rotate_leader_runbook'
require 'restart_node_runbook'

Runbook.book "Restart Cluster" do
  section "setup" do
    add RotateLeaderRunbook.input_step
    add RestartLeaderRunbook.input_step
  end

  add RotateLeaderRunbook.rotate_leader_section
  add RotateLeaderRunbook.restart_node_section
end
# lib/runbook/rotate_leader_runbook.rb

module RotateLeaderRunbook
  def self.input_step
    Runbook.step do
      ask "Server?", into: server
      ask "Timeout?", into: timeout
    end
  end

  def self.rotate_leader_section
    Runbook.section "Rotate Leader" do
      # ...
    end
  end
end

This way, you could coalesce inputs to all execute at the beginning of the runbook.

I'm also going to mention that I am planning to include this feature in the next version of Runbook: https://github.com/braintree/runbook/blob/master/TODO.md#always-executed-setup-section. This may not directly address your issue, but may be useful for your mega runbook o' doom.