Skip to content

Instantly share code, notes, and snippets.

@ChrisLundquist
Created July 11, 2012 22:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChrisLundquist/3093930 to your computer and use it in GitHub Desktop.
Save ChrisLundquist/3093930 to your computer and use it in GitHub Desktop.
root@collectd03:/opt/razor# git remote update
Fetching origin
Fetching bluebox
Fetching lynxbat
root@collectd03:/opt/razor# git diff lynxbat/master
diff --git a/bin/razor b/bin/razor
index 910ac06..8d0f080 100755
--- a/bin/razor
+++ b/bin/razor
@@ -1,193 +1,7 @@
#!/usr/bin/env ruby
-#
-# CLI Control for ProjectRazor
-# Format will be 'project_razor [module namespace] [module args{}]'
-#
-# We first add our Lib path to the load path. This is for non-gem ease of use
lib_path = File.dirname(File.expand_path(__FILE__)).sub(/\/bin$/,"/lib")
$LOAD_PATH.unshift(lib_path)
-
-require 'rubygems' if RUBY_VERSION < '1.9'
-# We require the root lib
-require "project_razor"
-
-# We need these for misc operations later
-require "json"
-require "colored"
-require "optparse"
-
-# We set a constant for our Slice root Namespace. We use this to pull the slice names back out
-# from objectspace
-SLICE_PREFIX = "ProjectRazor::Slice::"
-
-#
-def init
- @obj = ProjectRazor::Object.new
- @version = @obj.get_razor_version
- @logger = @obj.get_logger
- get_slices_loaded
-end
-
-# Call the slice
-def call_razor_slice
- begin
- if is_slice?
- razor_module = Object.full_const_get(SLICE_PREFIX + @namespace.capitalize).new(@args)
- razor_module.web_command = @web_command
- razor_module.verbose = @verbose
- razor_module.debug = @debug
- razor_module.slice_call
- else
- if @web_command
- puts JSON.dump({"slice" => "ProjectRazor::Slice", "result" => "InvalidSlice", "http_err_code" => 404})
- else
- if @namespace
- print "\n [#{@namespace.capitalize}] ".red
- print "<-Invalid Slice \n".yellow
- end
- end
- end
- rescue => e
- @logger.error "Razor slice error: #{e.message}"
- if @namespace
- print "\n [#{@namespace.capitalize}] ".red
- print "<-#{e.message} \n".yellow
- else
- print "\nMust Enter Slice Name \n".yellow
- end
- raise e if @debug
- end
-end
-
-# Load slices
-def get_slices_loaded
- temp_hash = {}
-
- slice_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib/project_razor/slice/*.rb"))
-
- Dir.glob(slice_path) do |f|
- name = File.basename(f,File.extname(f)).capitalize
- temp_hash[SLICE_PREFIX+name] = name unless name == 'Slice'
- end
-
- @slice_array = {}
- temp_hash.each_value {|x| @slice_array[x] = x}
- @slice_array = @slice_array.each_value.collect {|x| x}
-end
-
-# Validate slice
-def is_slice?
- @slice_array.each { |slice| return true if @namespace.downcase == slice.downcase } if @namespace
- false
-end
-
-def print_loaded_slices
- print "\nLoaded slices:\n\t"
- x = 1
- @slice_array.sort.each do |slice|
- slice_obj = ::Object.full_const_get(SLICE_PREFIX + slice.capitalize).new([])
- unless slice_obj.hidden
- print "[#{slice.downcase}] ".white
- if x > 5
- print "\n\t"
- x = 0
- end
- x += 1
- end
- end
- print "\n"
-end
-
-def get_optparse
-
- OptionParser.new do |opts|
- opts.banner = "\n" + "Razor - #{@version}".bold.green
- opts.separator "Usage: "
- opts.separator "razor [slice name] [command argument] [command argument]...".red
- opts.separator ""
- opts.separator "Switches".yellow
-
- @options[:verbose] = false
- opts.on( '-v', '--verbose', 'Enables verbose object printing'.yellow ) do
- @options[:verbose] = true
- end
-
- @options[:debug] = false
- opts.on( '-d', '--debug', 'Enables printing proper Ruby stacktrace'.yellow ) do
- @options[:debug] = true
- end
-
- @options[:webcommand] = false
- opts.on( '-w', '--webcommand', 'Accepts web commands.'.yellow ) do
- @options[:webcommand] = true
- end
-
- @options[:nocolor] = false
- opts.on( '-n', '--no-color', 'Disables console color. Useful for script wrapping.'.yellow ) do
- @options[:nocolor] = true
- end
-
- opts.on_tail( '-h', '--help', 'Display this screen'.yellow ) do
- puts opts
- print_loaded_slices
- exit
- end
- end
-end
-
-def get_first_args
- f = []
- ARGV.each do |a|
- if a.start_with?("-")
- f << a
- else
- return f
- end
- end
- f
-end
-# Initialise some values
-init
-first_args = get_first_args
-(0..first_args.count-1).each {ARGV.shift} if first_args.count > 0
-@options = {}
-optparse = get_optparse
-begin
- optparse.parse(first_args)
-rescue OptionParser::InvalidOption => e
- # We may use this option later so we will continue
- #puts e.message
- #puts optparse
- #exit
-end
-
-@web_command = @options[:webcommand]
-@debug = @options[:debug]
-@verbose = @options[:verbose]
-
-if @options[:nocolor] or !STDOUT.tty?
- # if this flag is set, override the default behavior of the underlying "colorize" method
- # from the "Colored" module so that it just returns the string that was passed into it
- # (this will have the effect of turning off any color that might be included in any of
- # the output statements involving Strings in Razor)
- module Colored
- extend self
- def colorize(string, options = {})
- string
- end
- end
- String.send(:include, Colored)
- optparse = get_optparse # reload optparse with color disabled
-end
-
-if ARGV.empty? && !@web_command
- puts optparse
- print_loaded_slices
-end
-
-@namespace = ARGV.shift
-@args = ARGV
-
-call_razor_slice
+require 'project_razor/cli'
+ProjectRazor::Cli.run(ARGV.dup)
diff --git a/lib/project_razor/cli.rb b/lib/project_razor/cli.rb
new file mode 100644
index 0000000..ca3f2ab
--- /dev/null
+++ b/lib/project_razor/cli.rb
@@ -0,0 +1,133 @@
+require 'rubygems' if RUBY_VERSION < '1.9'
+
+require 'colored'
+require 'json'
+require 'optparse'
+require 'project_razor'
+
+module ProjectRazor
+ class Cli
+ attr_reader :namespace
+ attr_reader :action
+ attr_reader :args
+ attr_reader :options
+
+ SLICE_PREFIX = "ProjectRazor::Slice::"
+
+ def self.run(args)
+ exit(new(args, $stdout).run)
+ end
+
+ def initialize(args=[], output=$stdout)
+ @obj = ProjectRazor::Object.new
+ @version = @obj.get_razor_version
+ @logger = @obj.get_logger
+
+ @args = args
+ @output = output
+ @options = {
+ :colorize => STDOUT.tty?,
+ :verbose => false,
+ :debug => false,
+ :webcommand => false,
+ }
+ @exit_code = 0
+ end
+
+ def opts
+ opts_parser = OptionParser.new do |opts|
+ opts.banner = "Usage: "+"razor [global options] [slice] [command] [command options] ...".red
+ opts.separator ""
+ opts.separator "Global Options:".yellow
+
+ opts.on('-v', '--verbose', 'Enable object verbose output.') { @options[:verbose] = true }
+ opts.on('-d', '--debug', 'Enable Ruby stack trace output.') { @options[:debug] = true }
+ opts.on('-w', '--webcommand', 'Accept web commands.') { @options[:webcommand] = true }
+ opts.on('-n', '--no-color', 'Disable console color.') { @options[:colorize] = false; require 'project_razor/cli/colored' }
+
+ opts.on_tail('-h', '--help', 'This help message.') { display_help; @exit_code=129; exit(@exit_code) }
+ end
+ end
+
+ def parse_options!
+ @args = opts.order!(@args)
+ self
+ end
+
+ def parse_slice!
+ slice = @args.shift
+ @namespace = slice
+ end
+
+ def display_usage
+ puts opts
+ end
+
+ def display_help
+ puts "\nRazor - #{@version}".bold.green
+ display_usage
+ puts "\n"
+ puts "Available Slices:"
+ puts "\t" + cli_slices.keys.collect { |x| "[#{x}]".white }.join(' ')
+ end
+
+ def puts(*val)
+ @output.puts val
+ end
+
+ def available_slices
+ slices = Hash[ ProjectRazor::Slice.class_children.map{|s| [s.to_s.gsub(SLICE_PREFIX,'').downcase, s] } ]
+ end
+
+ def cli_slices
+ slices = available_slices
+ slices.delete_if { |k, v| v.new([]).hidden }
+ end
+
+ def run
+ trap('TERM') { print "\nTerminated\n"; exit(1) }
+
+ parse_options!
+ # Disable color output.
+ require 'project_razor/cli/colored' unless @options[:colorize]
+ parse_slice!
+
+ if @namespace && available_slices.has_key?(@namespace)
+ slice = available_slices[@namespace].new(@args)
+ slice.web_command = @options[:webcommand]
+ slice.verbose = @options[:verbose]
+ slice.debug= @options[:debug]
+ slice.slice_call
+ else
+ if @options[:webcommand]
+ puts JSON.dump({"slice" => "ProjectRazor::Slice", "result" => "InvalidSlice", "http_err_code" => 404})
+ @exit_code = 1
+ else
+ puts "[#{@namespace}]".red + " <- Invalid Slice".yellow if @namespace
+ display_help
+ @exit_code = 1
+ end
+ end
+ rescue OptionParser::InvalidOption => e
+ unless @options[:webcommand]
+ puts(e.backtrace) if @options[:debug]
+ puts(e.message.red)
+ # if we successfully loaded the slice, print the slice options
+ if slice && slice.respond_to?(:command_opts)
+ puts(slice.command_opts)
+ else
+ puts(opts)
+ end
+ end
+ @exit_code = 129
+ rescue Exception => e
+ unless @options[:webcommand]
+ puts(e.backtrace) if @options[:debug]
+ puts(e.message.red)
+ end
+ @exit_code = 1
+ ensure
+ return @exit_code
+ end
+ end
+end
diff --git a/lib/project_razor/cli/colored.rb b/lib/project_razor/cli/colored.rb
new file mode 100644
index 0000000..45d8101
--- /dev/null
+++ b/lib/project_razor/cli/colored.rb
@@ -0,0 +1,7 @@
+module Colored
+ extend self
+ def colorize(string, options = {})
+ string
+ end
+end
+String.send(:include, Colored)
diff --git a/lib/project_razor/cli/version.rb b/lib/project_razor/cli/version.rb
new file mode 100644
index 0000000..93adfa4
--- /dev/null
+++ b/lib/project_razor/cli/version.rb
@@ -0,0 +1,6 @@
+module Project_Razor
+ module Cli
+ VERSION = '0.1.6.0'
+ end
+end
+
diff --git a/lib/project_razor/model/ubuntu/oneiric/preseed.erb b/lib/project_razor/model/ubuntu/oneiric/preseed.erb
index b744916..4c87654 100644
--- a/lib/project_razor/model/ubuntu/oneiric/preseed.erb
+++ b/lib/project_razor/model/ubuntu/oneiric/preseed.erb
@@ -18,6 +18,7 @@ d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
+d-i partman-auto/purge_lvm_from_device boolean true
d-i partman/default_filesystem string ext4
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
diff --git a/lib/project_razor/model/ubuntu/precise/preseed.erb b/lib/project_razor/model/ubuntu/precise/preseed.erb
index 305200f..ee0e428 100644
--- a/lib/project_razor/model/ubuntu/precise/preseed.erb
+++ b/lib/project_razor/model/ubuntu/precise/preseed.erb
@@ -18,6 +18,7 @@ d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
+d-i partman-auto/purge_lvm_from_device boolean true
d-i partman/default_filesystem string ext4
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2e5c829..550f812 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -3,5 +3,6 @@ $LOAD_PATH.unshift File.expand_path(File.join(dir, '..', 'lib'))
require 'project_razor'
require 'rspec'
+require 'mocha'
require 'json'
require 'net/http'
diff --git a/spec/unit/cli_spec.rb b/spec/unit/cli_spec.rb
new file mode 100644
index 0000000..7c7c1c6
--- /dev/null
+++ b/spec/unit/cli_spec.rb
@@ -0,0 +1,145 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+require 'project_razor/cli'
+
+describe ProjectRazor::Cli do
+
+ output = $stdout
+
+ describe 'when parsing global options' do
+ it 'should disable color for non-tty STDOUT' do
+ args = ''
+ STDOUT.stub(:tty?).and_return(false)
+ cli = ProjectRazor::Cli.new(args.split, output).parse_options!
+ cli.options[:colorize].should == false
+ end
+
+ it 'should disable color when specified' do
+ args = '--no-color'
+ cli = ProjectRazor::Cli.new(args.split, output).parse_options!
+ cli.options[:colorize].should == false
+ end
+
+ [ '--debug',
+ '-d'
+ ].each do |args|
+ it 'should enable debug when specified' do
+ cli = ProjectRazor::Cli.new(args.split, output).parse_options!
+ cli.options[:debug].should == true
+ end
+ end
+
+ [ '--verbose',
+ '-v'
+ ].each do |args|
+ it 'should enable verbose when specified' do
+ cli = ProjectRazor::Cli.new(args.split, output).parse_options!
+ cli.options[:verbose].should == true
+ end
+ end
+
+ [ '--webcommand',
+ '-w'
+ ].each do |args|
+ it 'should enable webcommand when specified' do
+ cli = ProjectRazor::Cli.new(args.split, output).parse_options!
+ cli.options[:webcommand].should == true
+ end
+ end
+
+ it 'should parse global options and parse slices' do
+ args = '-d image'
+
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.parse_options!
+ cli.parse_slice!
+ cli.namespace.should == 'image'
+ end
+
+ it 'should throw exception on unknown options' do
+ args = '-z'
+ expect { ProjectRazor::Cli.new(args.split, output).parse_options! }.should raise_error(OptionParser::InvalidOption)
+ end
+
+ it 'should suppress exceptions for webcommand' do
+ args = '-w -z'
+ output = mock
+ output.expects(:puts).never
+ cli = ProjectRazor::Cli.new(args.split, output).run
+ cli.should == 129
+ end
+ end
+
+ describe 'when loading slices' do
+ it 'should ignore hidden slices for cli' do
+ args = ''
+ cli = ProjectRazor::Cli.new(args.split, output).cli_slices
+ cli.keys.sort.should == %w( bmc broker image log model node policy tag )
+ end
+
+ it 'should find all availble slices' do
+ args = ''
+ cli = ProjectRazor::Cli.new(args.split, output).available_slices
+ cli.keys.sort.should == %w(base bmc boot broker config image log model node policy tag)
+ end
+
+ it 'should display help when no options provided' do
+ args = ''
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.should_receive(:display_help)
+ cli.run.should == 1
+ end
+
+ it 'should display help for invalid slices for cli' do
+ args = '-d zzz'
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.should_receive(:puts).with(/Invalid Slice/).once
+ cli.should_receive(:display_help)
+ cli.run.should == 1
+ end
+
+ it 'should return json for invalid slices for webcommand' do
+ args = '-w zzz'
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.should_receive(:puts).with(JSON.dump({ "slice" => "ProjectRazor::Slice", "result" => "InvalidSlice", "http_err_code" => 404 }))
+ cli.should_receive(:display_help).never
+ cli.run.should == 1
+ end
+
+ before :each do
+ @image = mock(ProjectRazor::Slice::Image)
+ @image.stub(:web_command=)
+ @image.stub(:verbose=)
+ @image.stub(:debug=)
+ @image.stub(:slice_call)
+ end
+
+ it 'should invoke available slices' do
+ args = 'image'
+
+ ProjectRazor::Slice::Image.should_receive(:new).with([]).and_return(@image)
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.stubs(:available_slices).returns({'image'=>ProjectRazor::Slice::Image})
+ cli.run.should == 0
+ end
+
+ it 'should invoke available slices with command options' do
+ args = 'image --help -f baz'
+
+ ProjectRazor::Slice::Image.should_receive(:new).with(['--help', '-f', 'baz']).and_return(@image)
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.stubs(:available_slices).returns({'image'=>ProjectRazor::Slice::Image})
+ cli.run.should == 0
+ end
+
+ it 'should invoke available slices after stripping global options' do
+ args = '-d image -d -f baz'
+
+ ProjectRazor::Slice::Image.should_receive(:new).with(['-d', '-f', 'baz']).and_return(@image)
+ cli = ProjectRazor::Cli.new(args.split, $stdout)
+ cli.stubs(:available_slices).returns({'image'=>ProjectRazor::Slice::Image})
+ cli.run.should == 0
+ end
+ end
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment