befog 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/befog CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'befog' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
2
8
 
3
- require "rubygems"
4
- require "bundler/setup"
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
5
12
 
6
- # resolve bin path, ignoring symlinks
7
- require "pathname"
8
- bin_file = Pathname.new(__FILE__).realpath
13
+ require 'rubygems'
14
+ require 'bundler/setup'
9
15
 
10
- # add self to libpath
11
- $:.unshift File.expand_path("../../lib", bin_file)
12
-
13
- require "befog"
14
-
15
- Befog::CLI.run(*ARGV)
16
+ load Gem.bin_path('befog', 'befog')
data/lib/befog/cli.rb CHANGED
@@ -5,16 +5,20 @@ require "befog/commands/stop"
5
5
  require "befog/commands/run"
6
6
  require "befog/commands/list"
7
7
  require "befog/commands/configure"
8
+ require "befog/commands/mixins/safely"
8
9
 
9
10
  module Befog
10
11
 
11
12
  module CLI
12
13
 
13
14
  class Error < RuntimeError ; end
14
-
15
+
16
+ extend Befog::Commands::Mixins::Safely
17
+
15
18
  COMMANDS = {
16
19
  "add" => Befog::Commands::Add,
17
20
  "remove" => Befog::Commands::Remove,
21
+ "rm" => Befog::Commands::Remove,
18
22
  "start" => Befog::Commands::Start,
19
23
  "stop" => Befog::Commands::Stop,
20
24
  "run" => Befog::Commands::Run,
@@ -24,35 +28,39 @@ module Befog
24
28
  "config" => Befog::Commands::Configure
25
29
  }
26
30
 
27
- def self.run(subcommand=nil,*args)
28
- if command = COMMANDS[subcommand]
29
- begin
30
- command.run(args)
31
-
32
- # TODO: use a befog-specific error class to
33
- # differentiate between expected exceptions
34
- # (just display the error message) and un-
35
- # expected (display the backtrace)
36
- rescue Befog::CLI::Error => e
37
- $stderr.puts "befog: #{e.message}"
38
- exit(-1)
39
- rescue => e # uh-oh
40
- $stderr.puts "Unexpected error"
41
- $stderr.puts "#{e.class}: #{e.message}"
42
- $stderr.puts e.backtrace
43
- exit(-1)
44
- end
45
- else
46
- if subcommand
47
- usage "'#{subcommand}' is not a supported command"
31
+ def self.parse(arguments)
32
+ options = {}
33
+ key = :bank
34
+ while not arguments.empty?
35
+ argument = arguments.shift
36
+ flag, short, long = /^(?:\-(\w)|\-\-(\w+))$/.match(argument).to_a
37
+ if flag
38
+ key = (short or long).to_sym
39
+ options[key] = true
48
40
  else
49
- usage "No subcommand provided."
41
+ case options[key]
42
+ when Array then options[key] << argument
43
+ when String then options[key] = [ options[key], argument ]
44
+ when true, nil then options[key] = argument
45
+ end
50
46
  end
51
47
  end
48
+ return options
49
+ end
50
+
51
+ def self.run(arguments)
52
+ subcommand = arguments.shift
53
+ if subcommand && (command = COMMANDS[subcommand])
54
+ command.run(CLI.parse(arguments))
55
+ elsif subcommand
56
+ usage "'#{subcommand}' is not a supported command"
57
+ else
58
+ usage
59
+ end
52
60
  end
53
61
 
54
- def self.usage(message)
55
- $stderr.puts "befog: #{message}"
62
+ def self.usage(message=nil)
63
+ $stderr.puts "befog: #{message}" if message
56
64
  $stderr.puts <<-eos
57
65
 
58
66
  Usage: befog <subcommand> [<bank>] [<options>]
@@ -66,14 +74,13 @@ Adds 3 servers to the bank "web-servers"
66
74
 
67
75
  Valid commands:
68
76
 
69
- configure Configure a bank of servers
70
- config
71
- add Provision new servers for a bank of servers
72
- remove De-provision servers for a bank of servers
73
- start Start a bank of servers
74
- stop Stop (suspend) a bank of servers
75
- run Run a command on each of a bank of servers
76
- list,ls List all servers with optional bank, region, or provider
77
+ configure, config Configure a bank of servers
78
+ add Provision new servers for a bank of servers
79
+ remove, rm De-provision servers
80
+ start Start servers
81
+ stop Stop (suspend) servers
82
+ run Run a command on each of a bank of servers
83
+ list, ls List servers
77
84
 
78
85
  You can get more options for any command with --help or -h.
79
86
  eos
@@ -1,8 +1,9 @@
1
1
  require "befog/commands/mixins/command"
2
2
  require "befog/commands/mixins/configurable"
3
- require "befog/commands/mixins/bank"
4
- require "befog/commands/mixins/provider"
3
+ require "befog/commands/mixins/scope"
4
+ require "befog/commands/mixins/safely"
5
5
  require "befog/commands/mixins/help"
6
+ require "befog/commands/mixins/traceable"
6
7
 
7
8
  module Befog
8
9
  module Commands
@@ -11,47 +12,80 @@ module Befog
11
12
 
12
13
  include Mixins::Command
13
14
  include Mixins::Configurable
14
- include Mixins::Bank
15
- include Mixins::Provider
15
+ include Mixins::Scope
16
16
  include Mixins::Help
17
+ include Mixins::Traceable
18
+ include Mixins::Safely
17
19
 
18
20
 
19
- command "befog add <bank>",
21
+ command :name => :add,
22
+ :usage => "befog add <bank> [<options>]",
20
23
  :default_to_help => true
21
24
 
22
25
  option :count,
23
- :short => "-c COUNT",
24
- :long => "--count COUNT",
26
+ :short => :c,
25
27
  :required => true,
26
28
  :description => "The number of machines to provision"
27
-
29
+
30
+ option :type,
31
+ :short => :t,
32
+ :description => "The type of machines to provision"
33
+
28
34
  def run
29
- count = options[:count].to_i
30
- if count <= 0
31
- $stderr.puts "Number must be an integer greater than 0."
32
- return
33
- end
34
- servers = []
35
+ provision_servers(options[:count].to_i)
36
+ end
37
+
38
+ def provision_servers(count)
39
+ error("Count must be greater than zero.") unless (count > 0)
40
+ provisioned = []; threads = []
35
41
  count.times do |i|
36
- $stdout.puts "Provisioning server #{i+1} for bank '#{options[:bank]}'..."
37
- # TODO: Figure out how to give the server a name
38
- # TODO: Check for values for all crucial configuration properties
39
- servers << compute.servers.create(
40
- :tags => {"Name" => options[:bank]}, :region => bank["region"],
41
- :flavor_id => bank["type"], :image_id => bank["image"],
42
- :security_group_ids => [options[:group]||"default"],
43
- :key_name => bank["keypair"])
44
- end
45
- $stdout.puts "This may take a few minutes ..."
46
- servers.each do |server|
47
- server.wait_for { $stdout.puts "Still working ..." ; ready? }
48
- self.servers << server.id
42
+ verbose <<-EOF.gsub(/^\s*/,"")
43
+ Provision server:
44
+ - region: #{region}
45
+ - type: #{flavor}
46
+ - image: #{image}
47
+ - security group: #{security_group}
48
+ - keypair: #{keypair}
49
+ EOF
50
+ threads << Thread.new do
51
+ safely do
52
+ unless rehearse?
53
+ log "Provisioning server #{i+1} for bank '#{bank_name}'..."
54
+ server = provision_server
55
+ server.wait_for { ready? }
56
+ provisioned << server
57
+ servers << server.id
58
+ end
59
+ end
60
+ end
49
61
  end
50
- servers.each do |server|
51
- $stdout.puts "Server #{server.id} is ready at #{server.dns_name}."
62
+ unless rehearse?
63
+ $stdout.print "This may take a few minutes .."
64
+ sleep 1 while threads.any? { |t| $stdout.print "."; $stdout.flush ; t.alive? }
65
+ $stdout.print "\n"
66
+ provisioned.each do |server|
67
+ log "Server #{server.id} is ready at #{server.dns_name}."
68
+ end
69
+ save
52
70
  end
53
- save
54
71
  end
72
+
73
+ def provision_server
74
+ compute.servers.create(
75
+ :tags => {"Name" => generate_server_name},
76
+ :region => region, :flavor_id => flavor, :image_id => image,
77
+ :security_group_ids => security_group, :key_name => keypair)
78
+ end
79
+
80
+ def generate_server_name
81
+ "#{bank_name}-#{generate_id}"
82
+ end
83
+
84
+ def generate_id
85
+ bank["counter"] ||= 0
86
+ bank["counter"] += 1
87
+ end
88
+
55
89
  end
56
90
  end
57
91
  end
@@ -1,7 +1,7 @@
1
1
  require "befog/commands/mixins/command"
2
2
  require "befog/commands/mixins/configurable"
3
- require "befog/commands/mixins/provider"
4
- require "befog/commands/mixins/bank"
3
+ require "befog/commands/mixins/scope"
4
+ require "befog/commands/mixins/safely"
5
5
  require "befog/commands/mixins/help"
6
6
 
7
7
  module Befog
@@ -12,74 +12,61 @@ module Befog
12
12
 
13
13
  include Mixins::Command
14
14
  include Mixins::Configurable
15
- include Mixins::Provider
16
- include Mixins::Bank
15
+ include Mixins::Scope
16
+ include Mixins::Safely
17
17
  include Mixins::Help
18
18
 
19
- command "befog configure [<bank>]",
19
+ command :name => :configure,
20
+ :usage => "befog configure [<bank>] [<options>]",
20
21
  :default_to_help => true
21
22
 
22
23
  option :key,
23
- :short => "-k KEY",
24
- :long => "--key KEY",
24
+ :short => :k,
25
25
  :description => "Your account key"
26
26
 
27
27
  option :secret,
28
- :short => "-s SECRET",
29
- :long => "--secret SECRET",
28
+ :short => :s,
30
29
  :description => "Your account secret"
31
30
 
32
31
  option :provider,
33
- :short => "-q PROVIDER",
34
- :long => "--provider PROVIDER",
32
+ :short => :q,
35
33
  :description => "The provider provisioning a bank of servers"
36
34
 
37
35
  option :region,
38
- :short => "-r REGION",
39
- :long => "--region REGION",
36
+ :short => :r,
40
37
  :description => "The region (datacenter) where a bank is provisioned"
41
38
 
42
39
  option :image,
43
- :short => "-i IMAGE",
44
- :long => "--image IMAGE",
40
+ :short => :i,
45
41
  :description => "The image for provisioning pods"
46
42
 
47
43
  option :keypair,
48
- :short => "-x KEYPAIR",
49
- :long => "--keypair KEYPAIR",
44
+ :short => :x,
50
45
  :description => "The keypair name to use with SSH"
51
46
 
52
47
  option :group,
53
- :short => "-g GROUP",
54
- :long => "--group GROUP",
48
+ :short => :g,
55
49
  :description => "The security group to use for new instances"
56
50
 
57
51
  option :type,
58
- :short => "-t TYPE",
59
- :long => "--type TYPE",
52
+ :short => :t,
60
53
  :description => "The number of machines to provision"
61
54
 
62
- def self.run(args)
63
- self.new(args).run
64
- end
65
-
66
- def initialize(arguments)
67
- process_arguments(arguments)
68
- end
69
-
70
55
  def run
71
- %w( key secret ).each do |key|
72
- _key = key.to_sym
73
- provider[key] = options[_key] if options[_key]
74
- end
75
- %w( region image keypair group type ).each do |key|
76
- _key = key.to_sym
77
- bank[key] = options[_key] if options[_key]
78
- end
79
- if options[:bank] and options[:provider]
80
- bank["provider"] = options[:provider]
56
+ safely do
57
+ %w( key secret ).each do |key|
58
+ _key = key.to_sym
59
+ provider[key] = options[_key] if options[_key]
60
+ end
61
+ %w( region image keypair group type ).each do |key|
62
+ _key = key.to_sym
63
+ bank[key] = options[_key] if options[_key]
64
+ end
65
+ if options[:bank] and options[:provider]
66
+ bank["provider"] = options[:provider]
67
+ end
68
+ save
81
69
  end
82
- save
83
70
  end
84
71
  end
85
72
  end
@@ -1,9 +1,9 @@
1
1
  require "fog"
2
2
  require "befog/commands/mixins/command"
3
3
  require "befog/commands/mixins/configurable"
4
- require "befog/commands/mixins/bank"
5
- require "befog/commands/mixins/provider"
6
- require "befog/commands/mixins/server"
4
+ require "befog/commands/mixins/scope"
5
+ require "befog/commands/mixins/safely"
6
+ require "befog/commands/mixins/selectable"
7
7
  require "befog/commands/mixins/help"
8
8
 
9
9
  module Befog
@@ -13,65 +13,27 @@ module Befog
13
13
 
14
14
  include Mixins::Command
15
15
  include Mixins::Configurable
16
- include Mixins::Bank
17
- include Mixins::Provider
18
- include Mixins::Server
16
+ include Mixins::Scope
17
+ include Mixins::Safely
18
+ include Mixins::Selectable
19
19
  include Mixins::Help
20
20
 
21
- command "befog list [<bank>]",
22
- :default_to_help => false
21
+ command :name => :list,
22
+ :usage => "befog list [<bank>] <options>",
23
+ :default_to_help => true
23
24
 
24
- option :provider,
25
- :short => "-q PROVIDER",
26
- :long => "--provider PROVIDER",
27
- :description => "The provider provisioning a bank of servers"
25
+
26
+ option :all,
27
+ :short => :a,
28
+ :description => "Deprovision all selected servers"
28
29
 
29
30
 
30
31
  def run
31
- if options[:bank]
32
- list_bank(options[:bank])
33
- elsif options[:provider]
34
- list_provider(options[:provider])
35
- else
36
- list_all
37
- end
38
- end
39
-
40
- def list_all
41
- $stdout.puts "All Servers"
42
- banks.keys.select do |name|
43
- list_bank(name)
44
- end
45
- end
46
-
47
- def list_provider(provider,indent="")
48
- $stdout.puts "#{indent}- Provider: #{provider}"
49
- indent += " "
50
- banks.select do |name,b|
51
- if b["configuration"] and b["configuration"]["provider"] == provider
52
- list_bank(name,indent)
53
- end
32
+ run_for_selected do |id|
33
+ server = compute.servers.get(id)
34
+ log "%-15s %-15s %-15s %-45s %-10s" % [id,server.flavor_id,server.tags["Name"],(server.dns_name||"-"),server.state]
54
35
  end
55
36
  end
56
-
57
- def list_bank(name, indent="")
58
- out = []
59
- out << "#{indent}- Bank: #{name}"
60
- indent += " "
61
- configuration = banks[name]["configuration"]
62
- out += %w( provider region image keypair type ).reduce([]) do |rval,key|
63
- rval << "#{key}: #{configuration[key]}" unless configuration[key].nil?
64
- rval
65
- end
66
- out << "instances:"
67
- banks[name]["servers"].each do |id|
68
- c = compute(configuration["provider"])
69
- s = c.servers.get(id)
70
- out << "- #{s.flavor_id} #{s.public_ip_address} #{s.state}"
71
- end
72
- $stdout.puts out.join("\n#{indent}")
73
- end
74
-
75
37
  end
76
38
  end
77
- end
39
+ end
@@ -1,4 +1,4 @@
1
- require "optparse"
1
+ require 'ostruct'
2
2
 
3
3
  module Befog
4
4
  module Commands
@@ -10,8 +10,8 @@ module Befog
10
10
 
11
11
  class << self
12
12
 
13
- def command(name,descriptor)
14
- @command = (Struct.new(:name,:descriptor)).new(name,descriptor)
13
+ def command(descriptor=nil)
14
+ descriptor ? (@command = OpenStruct.new(descriptor)) : @command
15
15
  end
16
16
 
17
17
  def option(name,descriptor)
@@ -20,43 +20,8 @@ module Befog
20
20
 
21
21
  def options ; @options||=[] ; end
22
22
 
23
- # TODO: Support multi-line descriptions?
24
- def process_arguments(arguments)
25
- results = {}; required = []
26
- parser = OptionParser.new do |parser|
27
- parser.banner = "Usage: #{@command.name} [options]"
28
- options.each do |name,descriptor|
29
- if descriptor[:help]
30
- parser.on_tail(*descriptor.values_at(:short,:long,:description)) do |value|
31
- $stdout.puts parser
32
- exit(0)
33
- end
34
- else
35
- results[name] = descriptor[:default] if descriptor[:default]
36
- required << name if descriptor[:required]
37
- parser.on(*descriptor.values_at(:short,:long,:description)) do |value|
38
- results[name] = value
39
- end
40
- end
41
- end
42
- end
43
- if @command.descriptor[:default_to_help] and arguments.empty?
44
- $stdout.puts parser
45
- exit(0)
46
- end
47
- parser.parse!(arguments)
48
- required.each do |name|
49
- unless results[name]
50
- $stderr.puts "Missing required option '#{name}'"
51
- $stderr.puts parser
52
- exit(-1)
53
- end
54
- end
55
- results
56
- end
57
-
58
- def run(arguments)
59
- new(arguments).run
23
+ def run(options)
24
+ new(options).run
60
25
  end
61
26
 
62
27
  end
@@ -66,12 +31,54 @@ module Befog
66
31
 
67
32
  attr_reader :options
68
33
 
69
- def initialize(arguments)
70
- process_arguments(arguments)
34
+ def initialize(_options)
35
+ safely do
36
+ @options = process_options(_options)
37
+ end
71
38
  end
72
-
73
- def process_arguments(arguments)
74
- @options = self.class.process_arguments(arguments)
39
+
40
+ def command
41
+ self.class.command
42
+ end
43
+
44
+ # TODO: Support multi-line descriptions?
45
+ def process_options(_options)
46
+ if (command.default_to_help and _options.empty?) or _options[:help]
47
+ usage
48
+ exit(0)
49
+ end
50
+ self.class.options.each do |name,descriptor|
51
+ short, required, default, type = descriptor.values_at(:short,:required,:default,:type)
52
+ _options[name] ||= (_options[short]||default)
53
+ _options.delete(short)
54
+ if required and not _options[name]
55
+ error("Missing required option --#{name}")
56
+ end
57
+ end
58
+ # TODO: add type conversion
59
+ return _options
60
+ end
61
+
62
+ def usage
63
+ $stderr.puts command.usage
64
+ usage = []
65
+ self.class.options.each do |name,descriptor|
66
+ short, required, default, description = descriptor.values_at(:short,:required,:default,:description)
67
+ required = (required ? "(required) " : "")
68
+ default = (default ? "(default: #{default}) " : "")
69
+ usage << "\t%-3s %-20s\t%-40s" % ["-#{short},", "--#{name} #{name.to_s.upcase}", "#{description} #{required}#{default}"]
70
+ end
71
+ $stderr.puts *(usage.sort)
72
+ end
73
+
74
+
75
+
76
+ def error(message)
77
+ raise CLI::Error.new(message)
78
+ end
79
+
80
+ def log(message)
81
+ $stdout.puts(message)
75
82
  end
76
83
  end
77
84
  end
@@ -8,16 +8,12 @@ module Befog
8
8
  target.module_eval do
9
9
 
10
10
  option :path,
11
- :short => "-p PATH",
12
- :long => "--path PATH",
13
- :default => "~/.befog",
14
- :description => "Path to the configuration file you want to use (defaults to '~/.befog')"
11
+ :short => :p, :default => "~/.befog",
12
+ :description => "Path to the configuration file"
15
13
 
16
14
  option :name,
17
- :short => "-n NAME",
18
- :long => "--name NAME",
19
- :default => "default",
20
- :description => "The name of this configuration (defaults to 'default')"
15
+ :short => :n, :default => "default",
16
+ :description => "The name of this configuration"
21
17
 
22
18
  end
23
19
  end
@@ -31,7 +27,11 @@ module Befog
31
27
  end
32
28
 
33
29
  def configuration
34
- _configuration[options[:name]] ||= {}
30
+ _configuration[configuration_name] ||= {}
31
+ end
32
+
33
+ def configuration_name
34
+ options[:name]
35
35
  end
36
36
 
37
37
  def save
@@ -5,10 +5,8 @@ module Befog
5
5
  def self.included(target)
6
6
  target.module_eval do
7
7
  option :help,
8
- :short => "-h",
9
- :long => "--help",
10
- :description => "Show this message",
11
- :help => true
8
+ :short => :h,
9
+ :description => "Show this message"
12
10
  end
13
11
  end
14
12
 
@@ -0,0 +1,28 @@
1
+ module Befog
2
+ module Commands
3
+ module Mixins
4
+ module Safely
5
+
6
+ def safely
7
+ begin
8
+ yield
9
+ rescue Befog::CLI::Error => e
10
+ $stderr.puts "befog #{command.name}: #{e.message}"
11
+ exit(-1)
12
+ rescue => e # uh-oh
13
+ $stderr.puts "Unexpected error"
14
+ $stderr.puts "#{e.class}: #{e.message}"
15
+ $stderr.puts e.backtrace
16
+ exit(-1)
17
+ end
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+
28
+