Open fabn opened 4 years ago
Currently I'm trying to configure a k8s cluster with this gem and I want to make a component optional. What I did until now is the following (just a proof of concept) but it doesn't follow the runbook approach and it doesn't work with auto mode obviously
section "Additional tools" do
step 'Install External DNS Chart' do
ruby_command do
prompt = TTY::Prompt.new
if prompt.yes?('Install External DNS Chart?')
options = case prompt.select("Choose your DNS Provider?", %w(DigitalOcean Cloudflare))
when 'DigitalOcean'
prompt.collect do
key('digitalocean.apiToken').ask('DigitalOcean Api Key:', required: true)
end
when 'Cloudflare'
prompt.collect do
key(:cloudflare) do
key(:email).ask('Cloudflare Email:', validate: :email, required: true)
key(:apiKey).ask('Cloudflare Api Key:', required: true)
end
end
else
raise 'Unnkown provider given'
end
puts "DNS options: #{options.inspect}"
command "helm install stable/external-dns #{dns_options}" # To be completed
end
end
end
end
It would be awesome to fully expose TTY::Prompt api in runbook, I don't know if it's possible.
Regarding your initial comment, this can be accomplished by extending the runbook DSL to include your own statements
Implementation for optional_step
is essentially a mashup of the confirm and ruby_command statements. It would go something like this:
$ runbook generate statement optional_step --root lib/runbook/extensions
# lib/runbook/extensions/optional_step.rb
module Runbook::Statements
class OptionalStep < Runbook::Statement
attr_reader :prompt, :block
def initialize(prompt, &block)
@prompt = prompt
@block = block
end
end
end
module RunbookOptionalStep
module RunbookExtensions
module OptionalStepMarkdown
def runbook__statements__optional_step(object, output, metadata)
# Format how your statement will be displayed when rendered with markdown
output << " if: #{object.prompt}\n"
output << " then run:\n"
output << " ```ruby\n"
begin
output << "#{deindent(object.block.source, padding: 3)}\n"
rescue ::MethodSource::SourceNotFoundError => e
output << " Unable to retrieve source code\n"
end
output << " ```\n\n"
end
end
Runbook::Views::Markdown.singleton_class.prepend(OptionalStepMarkdown)
module OptionalStepRun
def runbook__statements__optional_step(object, metadata)
auto = metadata[:auto]
if metadata[:noop]
metadata[:toolbox].output("[NOOP] Prompt: #{object.prompt}") unless auto
metadata[:toolbox].output("[NOOP] #{auto ? "Run" : "If yes, run"} the following Ruby block:\n")
begin
source = deindent(object.block.source)
metadata[:toolbox].output("```ruby\n#{source}\n```\n")
rescue ::MethodSource::SourceNotFoundError => e
metadata[:toolbox].output("Unable to retrieve source code")
end
return
end
if auto
metadata[:toolbox].output("Skipping confirmation (auto): #{object.prompt}")
# Not sure if the default should be execute or don't execute under auto
result = true
else
result = metadata[:toolbox].yes?(object.prompt)
end
return unless result
next_index = metadata[:index] + 1
parent_items = object.parent.items
remaining_items = parent_items.slice!(next_index..-1)
object.parent.dsl.instance_exec(object, metadata, &object.block)
parent_items[next_index..-1].each { |item| item.dynamic! }
parent_items.push(*remaining_items)
end
end
Runbook::Runs::SSHKit.singleton_class.prepend(OptionalStepRun)
end
end
Remember to include this in your Runbookfile
or equivalent config file.
This will allow you to execute a runbook like the following:
runbook = Runbook.book "Test Optional Step" do
section "Test Optional Step" do
step "Test me" do
optional_step "Exec the following?" do
puts "I've been executed"
end
end
end
end
One of the main use cases for the ruby_command
is a one-off way to define your own statements. The block has access to the ruby_command
object and the metadata
. This allows you to do pretty much anything with it that you can when defining your own statements. For example, you can do the following:
ruby_command do |rb_cmd, metadata|
prompt = metadata[:toolbox].prompt
if metadata[:auto] || prompt.yes?('Install External DNS Chart?')
# ...
end
I really like the approach of this gem but I'm wondering if there's a way to enable conditional steps or there's a way to present a menu to the user to choice from.
I.e. something like (pseudo code)
Since you're already using tty-prompt gem you have building blocks for that, so it should be a nice addition to this gem.