befog 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+