Ack, Config Parser works fine, the only issue with it is that it throws a horrible
exception when an invalid option is supplied. But I'm sure we can catch that at a
later date.
Structure looks fine, nice modular code.
Good Stuff,
Cheers
Martyn
----- Original Message -----
From: "Jason Guiditta" <jguiditt(a)redhat.com>
To: aeolus-devel(a)lists.fedorahosted.org
Sent: Friday, 3 June, 2011 9:58:47 PM
Subject: [PATCH conductor] Issue #1657: create basic structure for cli image tool.
This patch sets up the cli config parsing with tests, as well
as adding the basic structure for the library. New specs should
be added per Command class as things are implemented.
---
services/image_factory/aeolus-image/Rakefile | 46 +++++
.../image_factory/aeolus-image/bin/aeolus-image.rb | 6 +
.../image_factory/aeolus-image/lib/base_command.rb | 24 +++
.../aeolus-image/lib/build_command.rb | 9 +
.../aeolus-image/lib/config_parser.rb | 191 ++++++++++++++++++++
.../aeolus-image/lib/delete_command.rb | 9 +
.../aeolus-image/lib/import_command.rb | 9 +
.../image_factory/aeolus-image/lib/list_command.rb | 9 +
.../image_factory/aeolus-image/lib/push_command.rb | 9 +
.../aeolus-image/spec/config_parser_spec.rb | 52 ++++++
services/image_factory/aeolus-image/spec/spec.opts | 3 +
.../image_factory/aeolus-image/spec/spec_helper.rb | 27 +++
12 files changed, 394 insertions(+), 0 deletions(-)
create mode 100644 services/image_factory/aeolus-image/Rakefile
create mode 100755 services/image_factory/aeolus-image/bin/aeolus-image.rb
create mode 100644 services/image_factory/aeolus-image/lib/base_command.rb
create mode 100644 services/image_factory/aeolus-image/lib/build_command.rb
create mode 100644 services/image_factory/aeolus-image/lib/config_parser.rb
create mode 100644 services/image_factory/aeolus-image/lib/delete_command.rb
create mode 100644 services/image_factory/aeolus-image/lib/import_command.rb
create mode 100644 services/image_factory/aeolus-image/lib/list_command.rb
create mode 100644 services/image_factory/aeolus-image/lib/push_command.rb
create mode 100644 services/image_factory/aeolus-image/spec/config_parser_spec.rb
create mode 100644 services/image_factory/aeolus-image/spec/spec.opts
create mode 100644 services/image_factory/aeolus-image/spec/spec_helper.rb
diff --git a/services/image_factory/aeolus-image/Rakefile
b/services/image_factory/aeolus-image/Rakefile
new file mode 100644
index 0000000..d518ccf
--- /dev/null
+++ b/services/image_factory/aeolus-image/Rakefile
@@ -0,0 +1,46 @@
+require 'rubygems'
+require 'rake'
+require 'rake/clean'
+require 'rake/gempackagetask'
+require 'rake/rdoctask'
+require 'rake/testtask'
+require 'spec/rake/spectask'
+
+spec = Gem::Specification.new do |s|
+ s.name = 'aeolus-image'
+ s.version = '0.0.1'
+ s.has_rdoc = true
+ s.extra_rdoc_files = ['README', 'LICENSE']
+ s.summary = 'Your summary here'
+ s.description = s.summary
+ s.author = ''
+ s.email = ''
+ # s.executables = ['your_executable_here']
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
+ s.require_path = "lib"
+ s.bindir = "bin"
+end
+
+Rake::GemPackageTask.new(spec) do |p|
+ p.gem_spec = spec
+ p.need_tar = true
+ p.need_zip = true
+end
+
+Rake::RDocTask.new do |rdoc|
+ files =['README', 'LICENSE', 'lib/**/*.rb']
+ rdoc.rdoc_files.add(files)
+ rdoc.main = "README" # page to start on
+ rdoc.title = "aeolus-image Docs"
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
+ rdoc.options << '--line-numbers'
+end
+
+Rake::TestTask.new do |t|
+ t.test_files = FileList['test/**/*.rb']
+end
+
+Spec::Rake::SpecTask.new do |t|
+ t.spec_files = FileList['spec/**/*.rb']
+ t.libs << Dir["lib"]
+end
\ No newline at end of file
diff --git a/services/image_factory/aeolus-image/bin/aeolus-image.rb
b/services/image_factory/aeolus-image/bin/aeolus-image.rb
new file mode 100755
index 0000000..9c8c4ff
--- /dev/null
+++ b/services/image_factory/aeolus-image/bin/aeolus-image.rb
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+
+$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
+require 'config_parser.rb'
+parser = Aeolus::Image::ConfigParser.new(ARGV)
+puts parser.process
diff --git a/services/image_factory/aeolus-image/lib/base_command.rb
b/services/image_factory/aeolus-image/lib/base_command.rb
new file mode 100644
index 0000000..4d4fa42
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/base_command.rb
@@ -0,0 +1,24 @@
+module Aeolus
+ module Image
+ #This will house some methods that multiple Command classes need to use.
+ class BaseCommand
+ attr_accessor :options
+ def initialize(opts={}, logger=nil)
+ logger(logger)
+ @options = opts
+ end
+
+ protected
+ def logger(logger=nil)
+ @logger ||= logger
+ unless @logger
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::INFO
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ end
+ return @logger
+ end
+
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/build_command.rb
b/services/image_factory/aeolus-image/lib/build_command.rb
new file mode 100644
index 0000000..c000e64
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/build_command.rb
@@ -0,0 +1,9 @@
+module Aeolus
+ module Image
+ class BuildCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts={}, logger=nil)
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/config_parser.rb
b/services/image_factory/aeolus-image/lib/config_parser.rb
new file mode 100644
index 0000000..2f3fcc2
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/config_parser.rb
@@ -0,0 +1,191 @@
+require 'optparse'
+require 'logger'
+require 'base_command'
+require 'list_command'
+require 'build_command'
+require 'push_command'
+require 'import_command'
+require 'delete_command'
+
+module Aeolus
+ module Image
+ class ConfigParser
+ COMMANDS = %w(list build push import delete)
+ attr_accessor :options, :command, :args
+
+ def initialize(argv)
+ @args = argv
+ # Default options
+ @options = {}
+ parse
+ end
+
+ def process
+ # Check for command, then call appropriate Optionparser and initiate
+ # call to that class.
+ @command = @args.shift
+ # Eventually get the config file from user dir if it exists.
+ # File.expand_path("~")
+ if COMMANDS.include?(@command)
+ self.send((a)command.to_sym)
+ else
+ @args << "-h"
+ puts "Valid command required: \n\n"
+ parse
+ end
+ end
+
+ private
+ def parse
+ @optparse ||= OptionParser.new do|opts|
+ opts.banner = "Usage: aeolus-image [#{COMMANDS.join('|')}]
[general options] [command options]"
+
+ opts.separator ""
+ opts.separator "General options:"
+ opts.on('-u', '--user USERNAME', 'Conductor username')
do |user|
+ @options[:user] = user
+ end
+ opts.on('-w', '--password PASSWORD', 'Conductor
password') do |pw|
+ @options[:password] = pw
+ end
+ opts.on('-d', '--id ID', 'id for a given object') do
|id|
+ @options[:id] = id
+ end
+ opts.on('-r', '--provider NAME', 'name of specific provider
(ie ec2-us-east1)') do |name|
+ @options[:provider] = name
+ end
+ opts.on('-d', '--daemon', 'run as a background
process') do
+ @options[:subcommand] = :images
+ end
+ opts.on( '-h', '--help', 'Get usage information for this
tool') do
+ puts opts
+ exit(0)
+ end
+
+ opts.separator ""
+ opts.separator "List options:"
+ opts.on('-i', '--images', 'Retrieve a list of images')
do
+ @options[:subcommand] = :images
+ end
+ opts.on('-b', '--builds ID', 'Retrieve the builds of an
image') do |id|
+ @options[:subcommand] = :builds
+ @options[:id] = id
+ end
+ opts.on('-t', '--targetimages ID', 'Retrieve the target
images from a build') do |id|
+ @options[:subcommand] = :targetimages
+ @options[:id] = id
+ end
+ opts.on('-P', '--providerimages ID', 'Retrieve the provider
images from a target image') do |id|
+ @options[:subcommand] = :targetimages
+ @options[:id] = id
+ end
+ opts.on('-g', '--targets', 'Retrieve the values available
for the --target parameter') do
+ @options[:subcommand] = :targets
+ end
+ opts.on('-p', '--providers', 'Retrieve the values available
for the --provider parameter') do
+ @options[:subcommand] = :providers
+ end
+ opts.on('-a', '--accounts', 'Retrieve the values available
for the --account parameter') do
+ @options[:subcommand] = :accounts
+ end
+
+ opts.separator ""
+ opts.separator "Build options:"
+ opts.on('-T', '--target TARGET1,TARGET2', Array, 'provider
type (ec2, rackspace, rhevm, etc)') do |name|
+ @options[:target] = name
+ end
+ opts.on('-e', '--template FILE', 'path to file that
contains template xml') do |file|
+ @options[:template] = file
+ end
+ opts.on('-I', '--image ID', 'image to rebuild. When
specified w/o template and target, rebuilds from latest build') do |id|
+ @options[:image] = id
+ end
+
+ opts.separator ""
+ opts.separator "Push options:"
+ opts.on('-B', '--build ID', 'push all target images for a
build, to same providers as previously') do |id|
+ @options[:build] = id
+ end
+ opts.on('-I', '--image ID', 'push all the target images for
the latest build') do |id|
+ @options[:image] = id
+ end
+ opts.on('-A', '--account NAME', 'name of specific provider
account to use for push') do |name|
+ @options[:account] = name
+ end
+
+ opts.separator ""
+ opts.separator "Delete options:"
+ opts.on('-m', '--targetimage ID', 'delete target image and
its provider images') do |id|
+ @options[:targetimage] = id
+ end
+ opts.on('-D', '--providerimage ID', 'delete provider
image') do |id|
+ @options[:providerimage] = id
+ end
+
+ opts.separator ""
+ opts.separator "List Examples:"
+ opts.separator "aeolus-image list --images # list
available images"
+ opts.separator "aeolus-image list --builds $image_id # list the
builds of an image"
+ opts.separator "aeolus-image list --targetimages $build_id # list the
target images from a build"
+ opts.separator "aeolus-image list --targets # list the
values available for the --target parameter"
+ opts.separator "aeolus-image list --providers # list the
values available for the --provider parameter"
+ opts.separator "aeolus-image list --accounts # list the
values available for the --account parameter"
+
+ opts.separator ""
+ opts.separator "Build examples:"
+ opts.separator "aeolus-image build --target ec2 --template my.tmpl #
build a new image for ec2 from the template"
+ opts.separator "aeolus-image build --image $image_id #
rebuild the image template and targets from latest build"
+ opts.separator %q{aeolus-image build --target ec2,rackspace \ # rebuild
the image with a new template and set of targets
+ --image $image_i \
+ --template my.tmpl}
+
+ opts.separator ""
+ opts.separator "Push examples:"
+ opts.separator "aeolus-image push --provider ec2-us-east-1 --id
$target_image_id # push the target image to the specified provider"
+ opts.separator "aeolus-image push --build $build_id
# push all target images for a build, to same providers as previously"
+ opts.separator "aeolus-image push --account $provider_account --build
$build_id # ditto, using a specific provider account"
+ opts.separator "aeolus-image push --image $image_id
# push all the target images for the latest build"
+
+ opts.separator ""
+ opts.separator "Import examples:"
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --id $ami_id
# import an AMI from the specified provider"
+
+ opts.separator ""
+ opts.separator "Delete examples:"
+ opts.separator "aeolus-image delete --build $build_id #
deletes a build, updating latest/parent references as appropriate"
+ opts.separator "aeolus-image delete --targetimage $target_image #
deletes a target image and its provider images"
+ opts.separator "aeolus-image delete --providerimage $provider_image #
deletes a provider image"
+ end
+ @optparse.parse!(@args)
+ end
+
+ # TODO: Remove all this boilerplate and replace with some metaprogramming,
+ # perhaps method_missing
+ def list
+ # TODO: Instantiate and call object matching command type, for example:
+ # l = ListCommand.new(@options)
+ # Each Command will call it's own internal method depending on the contents
of the hash.
+ # For the list example above, that object would call a method 'images'
based on the item
+ # @options[:subcommand] being :images, so internally that class may do something
like:
+ # self.send(@options[:subcommand])
+ "Not implemented"
+ end
+
+ def build
+ "Not implemented"
+ end
+
+ def push
+ "Not implemented"
+ end
+
+ def import
+ "Not implemented"
+ end
+
+ def delete
+ "Not implemented"
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/delete_command.rb
b/services/image_factory/aeolus-image/lib/delete_command.rb
new file mode 100644
index 0000000..261563b
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/delete_command.rb
@@ -0,0 +1,9 @@
+module Aeolus
+ module Image
+ class DeleteCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts={}, logger=nil)
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/import_command.rb
b/services/image_factory/aeolus-image/lib/import_command.rb
new file mode 100644
index 0000000..e60498d
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/import_command.rb
@@ -0,0 +1,9 @@
+module Aeolus
+ module Image
+ class ImportCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts={}, logger=nil)
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/list_command.rb
b/services/image_factory/aeolus-image/lib/list_command.rb
new file mode 100644
index 0000000..5b19e77
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/list_command.rb
@@ -0,0 +1,9 @@
+module Aeolus
+ module Image
+ class ListCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts={}, logger=nil)
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/lib/push_command.rb
b/services/image_factory/aeolus-image/lib/push_command.rb
new file mode 100644
index 0000000..f3e4b51
--- /dev/null
+++ b/services/image_factory/aeolus-image/lib/push_command.rb
@@ -0,0 +1,9 @@
+module Aeolus
+ module Image
+ class PushCommand < BaseCommand
+ def initialize(opts={}, logger=nil)
+ super(opts={}, logger=nil)
+ end
+ end
+ end
+end
diff --git a/services/image_factory/aeolus-image/spec/config_parser_spec.rb
b/services/image_factory/aeolus-image/spec/config_parser_spec.rb
new file mode 100644
index 0000000..0b93409
--- /dev/null
+++ b/services/image_factory/aeolus-image/spec/config_parser_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+module Aeolus
+ module Image
+ describe ConfigParser do
+ it "should parse the specified command" do
+ config_parser = ConfigParser.new(%w(list))
+ config_parser.process
+ config_parser.command.should == 'list'
+ end
+
+ it "should notify the user of an invalid command" do
+ config_parser = ConfigParser.new(%w(sparkle))
+ config_parser.should_receive(:exit).with(0)
+ silence_stream(STDOUT) do
+ config_parser.process
+ end
+ end
+
+ it "should set options hash for valid general options" do
+ config_parser = ConfigParser.new(%w(list --user joe --password cloud --images))
+ config_parser.options[:user].should == 'joe'
+ config_parser.options[:password].should == 'cloud'
+ config_parser.options[:subcommand].should == :images
+ end
+
+ it "should set options hash for valid list options" do
+ config_parser = ConfigParser.new(%w(list --builds 12345))
+ config_parser.options[:subcommand].should == :builds
+ config_parser.options[:id].should == '12345'
+ end
+
+ it "should set options hash for valid build options" do
+ config_parser = ConfigParser.new(%w(build --target ec2,rackspace --image 12345
--template my.tmpl))
+ config_parser.options[:target].should == ['ec2','rackspace']
+ config_parser.options[:image].should == '12345'
+ config_parser.options[:template].should == 'my.tmpl'
+ end
+
+ it "should set options hash for valid push options" do
+ config_parser = ConfigParser.new(%w(push --provider ec2-us-east1 --id 12345))
+ config_parser.options[:provider].should == 'ec2-us-east1'
+ config_parser.options[:id].should == '12345'
+ end
+
+ it "should set options hash for valid delete options" do
+ config_parser = ConfigParser.new(%w(delete --build 12345))
+ config_parser.options[:build].should == '12345'
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/services/image_factory/aeolus-image/spec/spec.opts
b/services/image_factory/aeolus-image/spec/spec.opts
new file mode 100644
index 0000000..4154052
--- /dev/null
+++ b/services/image_factory/aeolus-image/spec/spec.opts
@@ -0,0 +1,3 @@
+--color
+--format specdoc
+--diff
\ No newline at end of file
diff --git a/services/image_factory/aeolus-image/spec/spec_helper.rb
b/services/image_factory/aeolus-image/spec/spec_helper.rb
new file mode 100644
index 0000000..52f5553
--- /dev/null
+++ b/services/image_factory/aeolus-image/spec/spec_helper.rb
@@ -0,0 +1,27 @@
+$: << File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
+require 'rubygems'
+require 'config_parser'
+
+module Helpers
+ # Silences any stream for the duration of the block.
+ #
+ # silence_stream(STDOUT) do
+ # puts 'This will never be seen'
+ # end
+ #
+ # puts 'But this will'
+ #
+ # (Taken from ActiveSupport)
+ def silence_stream(stream)
+ old_stream = stream.dup
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
+ stream.sync = true
+ yield
+ ensure
+ stream.reopen(old_stream)
+ end
+end
+
+Spec::Runner.configure do |config|
+ config.include Helpers
+end
--
1.7.4.4
_______________________________________________
aeolus-devel mailing list
aeolus-devel(a)lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/aeolus-devel