Permalink
Browse files

Awareness of embedded options, easier format for passed command-line …

…options, stdout feedback on options provided, enabled 'scout help', refined help text
  • Loading branch information...
1 parent 309e9c3 commit e24608bd82cbd68cd493a588a9f960a5905d9ec0 Andre Lewis committed Dec 10, 2009
Showing with 149 additions and 41 deletions.
  1. +1 −0 lib/scout.rb
  2. +16 −18 lib/scout/command.rb
  3. +0 −8 lib/scout/command/run.rb
  4. +35 −15 lib/scout/command/test.rb
  5. +17 −0 lib/scout/plugin.rb
  6. +80 −0 lib/scout/plugin_options.rb
View
@@ -6,4 +6,5 @@ module Scout
require "scout/command"
require "scout/plugin"
+require "scout/plugin_options"
require "scout/server"
View
@@ -39,23 +39,9 @@ def self.parse_options(argv)
opts.separator " Local plugin testing:"
opts.separator " #{program_name} [OPTIONS] test " +
"PATH_TO_PLUGIN [PLUGIN_OPTIONS]"
- opts.separator " Clone a client setup:"
- opts.separator " #{program_name} [OPTIONS] clone " +
- "CLIENT_KEY NEW_CLIENT_NAME"
- opts.separator ""
- opts.separator "CLIENT_KEY is the indentification key assigned to"
- opts.separator "this client by the server."
- opts.separator ""
- opts.separator "PATH_TO_PLUGIN is the file system path to a Ruby file"
- opts.separator "that contains a Scout plugin."
- opts.separator ""
- opts.separator "PLUGIN_OPTIONS can be the code for a Ruby Hash or the"
- opts.separator "path to a YAML options file containing defaults. These"
- opts.separator "options will be used for the plugin run."
- opts.separator ""
- opts.separator "NEW_CLIENT_NAME is name you wish to use for the new"
- opts.separator "client the server creates."
- opts.separator ""
+ opts.separator "[PLUGIN_OPTIONS] format: opt1=val1 opt2=val2 opt2=val3 etc."
+ opts.separator "Plugin will use internal defaults if options aren't provided."
+ opts.separator " "
opts.separator "Note: This client is meant to be installed and"
opts.separator "invoked through cron or any other scheduler."
opts.separator ""
@@ -74,7 +60,7 @@ def self.parse_options(argv)
end
opts.on( "-l", "--level LEVEL",
Logger::SEV_LABEL.map { |l| l.downcase },
- "The level of logging to report." ) do |level|
+ "The level of logging to report. Use -ldebug for most detail." ) do |level|
options[:level] = level
end
@@ -99,6 +85,16 @@ def self.parse_options(argv)
opts.on( "-F", "--force", "Force checkin to Scout server regardless of last checkin time") do |bool|
options[:force] = bool
end
+
+ opts.separator " "
+ opts.separator "Examples: "
+ opts.separator "1. Normal run (example key; use your own key):"
+ opts.separator " scout 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
+ opts.separator "2. Normal run with logging to standard out (example key; use your own key):"
+ opts.separator " scout --verbose 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
+ opts.separator "3. Test a plugin:"
+ opts.separator " scout test my_plugin.rb foo=18 bar=42"
+
end
begin
@@ -113,6 +109,8 @@ def self.parse_options(argv)
private_class_method :parse_options
def self.dispatch(argv)
+ # capture help command
+ argv.push("--help") if argv.first == 'help'
options = parse_options(argv)
command = if name_or_key = argv.shift
if cls = (Scout::Command.const_get(name_or_key.capitalize) rescue nil)
View
@@ -10,14 +10,6 @@ def run
@scout.load_history
@scout.fetch_plan
- # BEGIN: Experimental -- may not keep this.
- # Potential problems: increases complexity, messes with the history file outside a lock.
- if @scout.directives['reset_history']
- File.delete(history)
- @scout.create_blank_history
- @scout.load_history
- end
- # END: Experimental -- may not keep this
if @scout.new_plan || @scout.time_to_checkin? || @force
if @scout.new_plan
View
@@ -7,23 +7,42 @@ class Command
class Test < Command
def run
create_pid_file_or_exit
-
- plugin, options = @args
-
+ plugin, *provided_options = @args
# read the plugin_code from the file specified
plugin_code = File.read(plugin)
- plugin_options = if options.to_s[0, 1] == "{"
- eval(options) # options from command-line
- elsif options
- #
- # read the plugin_options from the YAML file specified,
- # parse each option and use the default value specified
- # in the options as the value to be passed to the test plugin
- #
- Hash[ *File.open(options) { |f| YAML.load(f) }["options"].
- map { |name, details| [name, details["default"]] }.flatten ]
+
+ options_for_run = {}
+
+ # deal with embedded options yaml
+ if options_yaml = Scout::Plugin.extract_options_yaml_from_code(plugin_code)
+ options=Scout::PluginOptions.from_yaml(options_yaml)
+
+ if options.error
+ puts "Problem parsing option definition in the plugin code (ignoring and continuing):"
+ puts options_yaml
+ else
+ puts "== Plugin options: "
+ puts options.to_s
+ options.select{|o|o.has_default?}.each{|o|options_for_run[o.name]=o.default}
+ end
+ else
+ puts "== This plugin doesn't have option metadata."
+ end
+
+ # provided_options are what the user gave us in the command line. Here, we merge them into
+ # the defaults we've already established (if any) for this run.
+ provided_options.each do |e|
+ if e.include?('=')
+ k,v=e.split('=',2)
+ options_for_run[k]=v
+ else
+ puts "ERROR: Option '#{e}' is no good -- provided options should be in the format name=value."
+ end
+ end
+ if options_for_run.any?
+ puts "== Running plugin with: #{options_for_run.to_a.map{|a| "#{a.first}=#{a.last}"}.join('; ') }"
else
- Hash.new
+ puts "== You haven't provided any options for running this plugin."
end
Scout::Server.new(nil, nil, history, log) do |scout|
@@ -32,8 +51,9 @@ def run
'plugin_id' => 1,
'name' => "Local Plugin",
'code' => plugin_code,
- 'options' => plugin_options,
+ 'options' => options_for_run,
'path' => plugin )
+ puts "== Output:"
scout.show_checkin(:pp)
end
end
View
@@ -1,7 +1,12 @@
#!/usr/bin/env ruby -wKU
+
module Scout
+
class Plugin
+
+ EMBEDDED_OPTIONS_REGEX = /OPTIONS ?= ?<<-?([A-Z_]+)(.*)\1/m
+
class << self
attr_accessor :last_defined
@@ -32,6 +37,18 @@ def needs(*libraries)
needs.push(*libraries.flatten)
end
end
+
+ # true if the code seems to have embedded options
+ def has_embedded_options?(code)
+ code =~ EMBEDDED_OPTIONS_REGEX
+ end
+
+ # extracts the internal YAML, if any, and returns the YAML string.
+ # returns nil if no embedded options.
+ def extract_options_yaml_from_code(code)
+ code =~ EMBEDDED_OPTIONS_REGEX
+ return $2
+ end
end
# Creates a new Scout Plugin to run.
@@ -0,0 +1,80 @@
+#!/usr/bin/env ruby -wKU
+
+require 'yaml'
+
+module Scout
+ # a data structure of an individual plugin option
+ class PluginOption
+ attr_reader :name, :notes, :default, :advanced, :password, :required
+ def initialize(name, h)
+ @name=name
+ @notes=h['notes'] || ''
+ @default=h['default'] || ''
+ @attributes=h['attributes'] || ''
+ @advanced = @attributes.include?('advanced')
+ @password = @attributes.include?('password')
+ @required = @attributes.include?('required')
+ end
+
+ # convenience -- for nicer syntax
+ def advanced?; @advanced; end
+ def password?; @password; end
+ def required?; @required; end
+ def has_default?; default != '';end
+
+ def to_s
+ required_string = required? ? " (required). " : ""
+ default_string = default == '' ? '' : " Default: #{default}. "
+ "'#{name}'#{required_string}#{default_string}#{notes}"
+ end
+ end
+
+ # A collection of pluginOption
+ # Create: opts=PluginOptions.from_yaml(yaml_string)
+ # Check if there were any problems -- opts.error -- should be nil.
+ #
+ # A valid options yaml looks like this:
+ # max_swap_used:
+ # notes: If swap is larger than this amount, an alert is generated. Amount should be in MB.
+ # default: 2048 # 2 GB
+ # max_swap_ratio:
+ # notes: If swap used over memory used is larger than this amount, an alert is generated
+ # default: 3
+ # attributes: required advanced
+ class PluginOptions < Array
+
+ attr_accessor :error
+
+ # Should be valid YAML, a hash of hashes ... if not, will be caught in the rescue below
+ def self.from_yaml(string)
+ options_array=[]
+ error=nil
+
+ items=YAML.load(string)
+ items.each_pair {|name, hash| options_array.push(PluginOption.new(name,hash)) }
+ rescue
+ error="Invalid Plugin Options"
+ ensure
+ res=PluginOptions.new(options_array)
+ res.error=error
+ return res
+ end
+
+ def advanced
+ select{|o|o.advanced? }
+ end
+
+ def regular
+ select{|o|!o.advanced? }
+ end
+
+ def to_s
+ res=[]
+ each_with_index do |opt,i|
+ res.push "#{i+1}. #{opt.to_s}"
+ end
+ res.join("\n")
+ end
+
+ end
+end

0 comments on commit e24608b

Please sign in to comment.