server-blender 0.0.14 → 0.0.15

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/.gitignore CHANGED
@@ -25,3 +25,4 @@ pkg
25
25
 
26
26
  ## PROJECT::SPECIFIC
27
27
  doc
28
+ log/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.14
1
+ 0.0.15
data/bin/blender CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path(File.join(__FILE__, "../../lib/blender"))
3
+ $:.unshift File.expand_path(File.join(__FILE__, "../../lib"))
4
+ require 'blender'
4
5
  include Blender
5
6
 
6
7
  # commands are just ruby scripts in lib/blender/cli
@@ -18,5 +19,5 @@ Run "blender COMMAND -h" to get help about a command
18
19
  end
19
20
 
20
21
  require 'optparse'
21
- require File.expand_path(File.join(__FILE__, "../../lib/blender/cli/", command))
22
- main
22
+ require "blender/cli/#{command}"
23
+ Blender::Cli.const_get(command.capitalize).new(ARGV).execute
data/files/bootstrap.sh CHANGED
@@ -70,6 +70,7 @@ function supported_version()
70
70
 
71
71
  case "`distribution`" in
72
72
  "Ubuntu 10.04") true;;
73
+ "Ubuntu 10.10") true;;
73
74
  *) false;;
74
75
  esac
75
76
  }
data/files/mix.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  set -ue
2
2
 
3
3
  SHADOW_PUPPET_VERSION="0.3.2"
4
- MANIFEST_VERSION="0.0.15"
4
+ MANIFEST_VERSION="0.0.17"
5
5
 
6
6
  trap "echo FAILED" EXIT
7
7
 
@@ -54,10 +54,10 @@ ensure_gem ruby-debug
54
54
  ensure_gem server-blender-manifest $MANIFEST_VERSION
55
55
  if run_recipe; then
56
56
  echo
57
- echo "Your ServerShake is ready. Have fun!"
57
+ echo "Your mix is ready. Have fun!"
58
58
  else
59
59
  echo
60
- echo "Failed to mix your ServerShake. Check error messages above for details"
60
+ echo "Mix failed. Check error messages above for details"
61
61
  fi
62
62
 
63
63
  trap - EXIT
data/lib/blender.rb CHANGED
File without changes
@@ -0,0 +1,13 @@
1
+ module Blender
2
+ class Cli
3
+ def initialize(args)
4
+ @args = args
5
+ end
6
+
7
+ def run(*cmd)
8
+ STDERR.puts ">> #{cmd * ' '}"
9
+ system(*cmd) unless @dry
10
+ end
11
+
12
+ end
13
+ end
@@ -1,57 +1,59 @@
1
- def parse_options
2
- options = {
3
- :system_gems => 'y'
4
- }
5
- OptionParser.new do |opts|
6
- opts.banner = "Usage: blender init [OPTIONS] HOST"
7
- opts.separator ""
8
- opts.separator "Common options:"
9
-
10
- opts.on("-u", "--upstream-gems", "don't use the system gems, download and install upstream version instead") do
11
- options[:system_gems] = 'n'
12
- end
13
-
14
- opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
15
- options[:node] = val
16
- end
1
+ require 'blender/cli'
2
+
3
+ class Blender::Cli::Init < Blender::Cli
4
+ def parse_options(args)
5
+ options = {
6
+ :system_gems => 'y'
7
+ }
8
+ opts = OptionParser.new do |opts|
9
+ opts.banner = "Usage: blender init [OPTIONS] HOST"
10
+ opts.separator ""
11
+ opts.separator "Common options:"
12
+
13
+ opts.on("-u", "--upstream-gems", "don't use the system gems, download and install upstream version instead") do
14
+ options[:system_gems] = 'n'
15
+ end
16
+
17
+ opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
18
+ options[:node] = val
19
+ end
20
+
21
+ opts.on("-t", "--trace", "dump trace to the stdout") do |val|
22
+ options[:trace] = true
23
+ end
24
+
25
+ opts.on("-H", "--hostname HOSTNAME", "set HOSTNAME") do |val|
26
+ options[:hostname] = val
27
+ end
28
+
29
+ opts.on("-h", "--help", "Show this message") do
30
+ raise(opts.to_s)
31
+ end
17
32
 
18
- opts.on("-t", "--trace", "dump trace to the stdout") do |val|
19
- options[:trace] = true
20
33
  end
34
+ opts.parse!(args)
21
35
 
22
- opts.on("-H", "--hostname HOSTNAME", "set HOSTNAME") do |val|
23
- options[:hostname] = val
24
- end
36
+ raise("please provide a hostname\n#{opts}") unless host = args.shift
25
37
 
26
- opts.on("-h", "--help", "Show this message") do
27
- raise(opts.to_s)
28
- end
38
+ raise("unexpected: #{args*" "}\n#{opts}") unless args.empty?
29
39
 
30
- end.parse!
40
+ options.merge(:host => host)
41
+ end
31
42
 
32
- raise("please provide a hostname\n#{opts}") unless host = ARGV.shift
43
+ def bootstrap(options)
44
+ extra=""
45
+ extra << " TRACE=1" if options[:trace]
46
+ extra << " HOSTNAME=#{options[:hostname]}" if options[:hostname]
47
+ extra << " NODE=#{options[:node]}" if options[:node]
33
48
 
34
- options.merge(:host => host)
35
- end
36
-
37
- def run(*cmd)
38
- STDERR.puts ">> #{cmd * ' '}"
39
- system(*cmd)
40
- end
41
-
42
- def bootstrap(options)
43
- extra=""
44
- extra << " TRACE=1" if options[:trace]
45
- extra << " HOSTNAME=#{options[:hostname]}" if options[:hostname]
46
- extra << " NODE=#{options[:node]}" if options[:node]
47
-
48
- run "cat #{File.expand_path("files/bootstrap.sh", Blender::ROOT)} | ssh #{options[:host]} USE_SYSTEM_GEMS=#{options[:system_gems]}#{extra} /bin/bash -eu"
49
- end
49
+ run "cat #{File.expand_path("files/bootstrap.sh", Blender::ROOT)} | ssh #{options[:host]} USE_SYSTEM_GEMS=#{options[:system_gems]}#{extra} /bin/bash -eu" or raise "failed bootstrap.sh"
50
+ end
50
51
 
51
- def main
52
- options = parse_options
53
- bootstrap(options)
52
+ def execute
53
+ options = parse_options(@args)
54
+ bootstrap(options)
54
55
 
55
- rescue => e
56
- abort(e.to_s)
56
+ rescue => e
57
+ abort(e.to_s)
58
+ end
57
59
  end
@@ -1,89 +1,89 @@
1
- def parse_options
2
- options = {}
3
- opts = OptionParser.new do |opts|
4
- opts.banner = "Usage: blender mix [OPTIONS] [DIR] HOST"
5
- opts.separator "Options:"
6
-
7
- opts.on("-r", "--recipe RECIPE", "if RECIPE is not specified blender will first look for <directory_name>.rb and then for blender-recipe.rb") do |val|
8
- options[:recipe] = val
9
- end
1
+ require 'blender/cli'
10
2
 
11
- opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
12
- options[:node] = val
13
- end
3
+ class Blender::Cli::Mix < Blender::Cli
14
4
 
15
- opts.on("-R", "--roles ROLES", "comma delimited list of roles that should execute") do |val|
16
- options[:roles] = val
17
- end
5
+ def parse_options(args)
6
+ options = {}
7
+ opts = OptionParser.new do |opts|
8
+ opts.banner = "Usage: blender mix [OPTIONS] [DIR] HOST"
9
+ opts.separator "Options:"
18
10
 
19
- opts.separator ""
20
- opts.separator "Common options:"
11
+ opts.on("-r", "--recipe RECIPE", "if RECIPE is not specified blender will first look for <directory_name>.rb and then for blender-recipe.rb") do |val|
12
+ options[:recipe] = val
13
+ end
21
14
 
22
- opts.on("-h", "--help", "Show this message") do
23
- raise(opts.to_s)
24
- end
15
+ opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
16
+ options[:node] = val
17
+ end
25
18
 
26
- opts.separator ""
27
- opts.separator "Notes:"
28
- opts.separator ' "." used if DIR not specified'
19
+ opts.on("-R", "--roles ROLES", "comma delimited list of roles that should execute") do |val|
20
+ options[:roles] = val
21
+ end
29
22
 
30
- end
31
- opts.parse!
23
+ opts.separator ""
24
+ opts.separator "Common options:"
32
25
 
33
- options[:usage] = opts.to_s
26
+ opts.on("-h", "--help", "Show this message") do
27
+ raise(opts.to_s)
28
+ end
34
29
 
35
- dir = ARGV.shift
36
- host = ARGV.shift
37
- raise("unexpected: #{ARGV*" "}\n#{opts}") unless ARGV.empty?
30
+ opts.separator ""
31
+ opts.separator "Notes:"
32
+ opts.separator ' "." used if DIR not specified'
38
33
 
39
- if host.nil?
40
- host = dir
41
- dir = "."
42
- end
34
+ end
35
+ opts.parse!(args)
43
36
 
44
- raise(opts.to_s) unless dir && host
37
+ options[:usage] = opts.to_s
45
38
 
46
- raise("#{dir} is not a directory\n#{opts}") unless File.directory?(dir)
39
+ dir = args.shift
40
+ host = args.shift
41
+ raise("unexpected: #{args*" "}\n#{opts}") unless args.empty?
47
42
 
48
- options.merge(:dir => dir, :host => host)
49
- end
43
+ if host.nil?
44
+ host = dir
45
+ dir = "."
46
+ end
50
47
 
48
+ raise(opts.to_s) unless dir && host
51
49
 
52
- def find_recipe(options)
53
- # check for recipe, recipe.rb, directory_name.rb, and default.rb
54
- recipes = []
55
- if rname = options[:recipe]
56
- recipes << rname << "#{rname}.rb"
50
+ raise("#{dir} is not a directory\n#{opts}") unless File.directory?(dir)
51
+
52
+ options.merge(:dir => dir, :host => host)
57
53
  end
58
- recipes << "#{File.basename(File.expand_path(options[:dir]))}.rb" << "blender-recipe.rb"
59
54
 
60
- recipe = recipes.detect {|r| File.file?(File.join(options[:dir], r))} ||
61
- raise("recipe not found (looking for #{recipes * ' '})\n#{options[:usage]}")
62
- end
63
55
 
64
- def run(*cmd)
65
- STDERR.puts ">> #{cmd * ' '}"
66
- system(*cmd)
67
- end
56
+ def find_recipe(options)
57
+ # check for recipe, recipe.rb, directory_name.rb, and blender-recipe.rb
58
+ if rname = options[:recipe]
59
+ recipes = [rname, "#{rname}.rb"]
60
+ else
61
+ recipes = ["#{File.basename(File.expand_path(options[:dir]))}.rb", "blender-recipe.rb"]
62
+ end
68
63
 
64
+ recipe = recipes.detect {|r| File.file?(File.join(options[:dir], r))} ||
65
+ raise("recipe not found (looking for #{recipes * ' '})\n#{options[:usage]}")
66
+ end
69
67
 
70
- def run_recipe(recipe, options)
71
- run "cat #{File.expand_path("files/init.sh", Blender::ROOT)} | ssh #{options[:host]} /bin/bash -l" or raise("failed init.sh")
68
+ def run_recipe(recipe, options)
69
+ run "cat #{File.expand_path("files/init.sh", Blender::ROOT)} | ssh #{options[:host]} /bin/bash -l" or raise("failed init.sh")
72
70
 
73
- run("rsync -qazP --delete --exclude '.*' #{options[:dir]}/ #{options[:host]}:/var/lib/blender/recipes") or raise("failed rsync")
71
+ run("rsync -qazP --delete --exclude '.*' #{options[:dir]}/ #{options[:host]}:/var/lib/blender/recipes") or raise("failed rsync")
74
72
 
75
- env_config = "RECIPE=#{recipe}"
76
- env_config << " NODE=#{options[:node]}" if options[:node]
77
- env_config << " ROLES=#{options[:roles]}" if options[:roles]
73
+ env_config = "RECIPE=#{recipe}"
74
+ env_config << " NODE=#{options[:node]}" if options[:node]
75
+ env_config << " ROLES=#{options[:roles]}" if options[:roles]
78
76
 
79
- run "cat #{File.expand_path("files/mix.sh", Blender::ROOT)} | ssh #{options[:host]} #{env_config} /bin/bash -l" or raise("failed mix.sh")
80
- end
77
+ run "cat #{File.expand_path("files/mix.sh", Blender::ROOT)} | ssh #{options[:host]} #{env_config} /bin/bash -l" or raise("failed mix.sh")
78
+ end
81
79
 
82
- def main
83
- options = parse_options
84
- recipe = find_recipe(options)
85
- run_recipe(recipe, options)
80
+ def execute
81
+ options = parse_options(@args)
82
+ recipe = find_recipe(options)
83
+ run_recipe(recipe, options)
84
+
85
+ rescue => e
86
+ abort(e.to_s)
87
+ end
86
88
 
87
- rescue => e
88
- abort(e.to_s)
89
89
  end
@@ -1,103 +1,104 @@
1
- require 'pp'
1
+ require 'blender/cli'
2
2
 
3
- def parse_options
4
- options = {}
3
+ class Blender::Cli::Start < Blender::Cli
5
4
 
6
5
  AMI_64 = "ami-55739e3c"
7
6
  AMI_32 = "ami-bb709dd2"
8
7
 
9
- OptionParser.new do |opts|
10
- opts.banner = "Usage: blender start [OPTIONS] [-- [ec2run options]]"
11
- opts.separator "Options:"
12
-
13
- opts.on("--ami AMI",
14
- "use specified AMI instead of the default one.",
15
- "If you don't specify your own AMI blender will choose a defaule one:",
16
- "* #{AMI_32} for 32 bits",
17
- "* #{AMI_64} for 64 bits",
18
- "You can change the defaults by writing your own AMIs",
19
- "into ~/.blender/ami and ~/.blender/ami64 files",
20
- " "
21
- ) do |val|
22
- options[:ami] = val
23
- end
24
- opts.on("--key KEY",
25
- "use KEY when starting instance. KEY should already be generated.",
26
- "If you don't specify a KEY blender will try to use the key from your EC2 account",
27
- "Note: There must be only ONE key on the account for it to work. ",
28
- " "
29
- ) do |val|
30
- options[:key] = val
31
- end
8
+ def parse_options(args)
9
+ options = {}
32
10
 
33
- opts.on("--64", "use 64 bit default AMI. This does nothing if you specify your own AMI") do
34
- options[64] = true
35
- end
11
+ opts = OptionParser.new do |opts|
12
+ opts.banner = "Usage: blender start [OPTIONS] [-- [ec2run options]]"
13
+ opts.separator "Options:"
36
14
 
37
- opts.on("-n", "--dry-run", "Don't do anything, just print the command line to be executed") do |val|
38
- options[:dry] = true
39
- end
15
+ opts.on("-a", "--ami AMI",
16
+ "use specified AMI instead of the default one.",
17
+ "If you don't specify your own AMI blender will choose a defaule one:",
18
+ "* #{AMI_32} for 32 bits",
19
+ "* #{AMI_64} for 64 bits",
20
+ "You can change the defaults by writing your own AMIs",
21
+ "into ~/.blender/ami and ~/.blender/ami64 files"
22
+ ) do |val|
23
+ options[:ami] = val
24
+ end
40
25
 
41
- opts.separator "\nCommon options:"
26
+ opts.separator ""
42
27
 
43
- opts.on("-h", "--help", "Show this message") do
44
- puts opts
45
- exit
46
- end
28
+ opts.on("-k", "--key KEY",
29
+ "use KEY when starting instance. KEY should already be generated.",
30
+ "If you don't specify a KEY blender will try to use the key from your EC2 account",
31
+ "Note: There must be only ONE key on the account for it to work."
32
+ ) do |val|
33
+ options[:key] = val
34
+ end
35
+ opts.separator ""
47
36
 
48
- opts.on_tail <<-EXAMPLE
37
+ opts.on("--64", "use 64 bit default AMI. This does nothing if you specify your own AMI") do
38
+ options[64] = true
39
+ end
49
40
 
50
- Example:
41
+ opts.on("-n", "--dry-run", "Don't do anything, just print the command line to be executed") do |val|
42
+ @dry = options[:dry] = true
43
+ end
51
44
 
52
- # start a 64bit instance with default options
53
- blender start -64
45
+ opts.separator "\nCommon options:"
54
46
 
55
- # start with a custom ami
56
- blender start --ami ami-2d4aa444
47
+ opts.on("-h", "--help", "Show this message") do
48
+ raise(opts.to_s)
49
+ end
57
50
 
58
- # start with passing arguments to ec2run: use security group default+test
59
- blender start -- -g default -g test
60
- EXAMPLE
51
+ opts.on_tail <<-EXAMPLE
61
52
 
53
+ Example:
62
54
 
63
- end.parse!
55
+ # start a 64bit instance with default options
56
+ blender start -64
57
+
58
+ # start with a custom ami
59
+ blender start --ami ami-2d4aa444
60
+
61
+ # start with passing arguments to ec2run: use security group default+test
62
+ blender start -- -g default -g test
63
+ EXAMPLE
64
+
65
+ end
66
+ opts.parse!(args)
64
67
 
68
+ raise("unexpected: #{args*" "}\n#{opts}") unless args.empty?
65
69
 
66
- options
67
- end
70
+ options
71
+ end
68
72
 
69
- def default_ami(options)
70
- name = options[64] ? "~/.blender/ami64" : "~/.blender/ami"
71
- ami = File.read(File.expand_path(name)) rescue nil
72
- ami = options[64] ? AMI_64 : AMI_32 if ami.nil? || ami.empty?
73
- ami
74
- end
73
+ def default_ami(options = {})
74
+ name = options[64] ? "~/.blender/ami64" : "~/.blender/ami"
75
+ ami = File.read(File.expand_path(name)).strip rescue nil
76
+ ami = options[64] ? AMI_64 : AMI_32 if ami.nil? || ami.empty?
77
+ ami
78
+ end
75
79
 
76
- def default_key(options)
77
- keys = `ec2dkey`.strip.split("\n")
78
- abort("too many keys") if keys.length > 1
79
- abort("can't find any keys") if keys.length != 1
80
- keys.first.split("\t")[1] || raise("invalid key")
81
- end
80
+ def default_key
81
+ keys = `ec2dkey`.strip.split("\n")
82
+ raise("too many keys") if keys.length > 1
83
+ raise("can't find any keys") if keys.length != 1
84
+ keys.first.split("\t")[1].strip || raise("invalid key")
85
+ end
82
86
 
83
- def run(*cmd)
84
- system(*cmd)
85
- end
87
+ def start_ami(options = {}, args = [])
88
+ ami = options[:ami] || default_ami(options)
89
+ key = options[:key] || default_key
86
90
 
87
- def start_ami(options)
88
- ami = options[:ami] || default_ami(options)
89
- key = options[:key] || default_key(options)
91
+ cmd = ["ec2run", ami, "-k", key, *args]
90
92
 
91
- cmd = ["ec2run", ami, "-k", key, *ARGV]
93
+ run(*cmd) or raise "failed to start ami"
94
+ end
92
95
 
93
- STDERR.puts ">> #{cmd * ' '}"
94
- run(cmd) unless options[:dry]
95
- end
96
+ def execute
97
+ options = parse_options(@args)
98
+ start_ami(options, *@args)
96
99
 
97
- def main
98
- options = parse_options
99
- start_ami(options)
100
+ rescue => e
101
+ abort(e.to_s)
102
+ end
100
103
 
101
- rescue => e
102
- abort(e.to_s)
103
104
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{server-blender}
8
- s.version = "0.0.14"
8
+ s.version = "0.0.15"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Vitaly Kushner"]
12
- s.date = %q{2010-07-04}
12
+ s.date = %q{2010-11-28}
13
13
  s.default_executable = %q{blender}
14
14
  s.description = %q{Boostrap and manage servers with shadow_puppet
15
15
 
@@ -39,21 +39,26 @@ http://reductivelabs.com/products/puppet/
39
39
  "files/init.sh",
40
40
  "files/mix.sh",
41
41
  "lib/blender.rb",
42
+ "lib/blender/cli.rb",
42
43
  "lib/blender/cli/init.rb",
43
44
  "lib/blender/cli/mix.rb",
44
45
  "lib/blender/cli/start.rb",
45
46
  "server-blender.gemspec",
46
- "spec/server-blender_spec.rb",
47
+ "spec/cli/init_spec.rb",
48
+ "spec/cli/mix_spec.rb",
49
+ "spec/cli/start_spec.rb",
47
50
  "spec/spec.opts",
48
51
  "spec/spec_helper.rb"
49
52
  ]
50
53
  s.homepage = %q{http://astrails.com/opensource/server-blender}
51
54
  s.rdoc_options = ["--charset=UTF-8"]
52
55
  s.require_paths = ["lib"]
53
- s.rubygems_version = %q{1.3.6}
56
+ s.rubygems_version = %q{1.3.7}
54
57
  s.summary = %q{Server provisioning and configuration management tool}
55
58
  s.test_files = [
56
- "spec/server-blender_spec.rb",
59
+ "spec/cli/init_spec.rb",
60
+ "spec/cli/mix_spec.rb",
61
+ "spec/cli/start_spec.rb",
57
62
  "spec/spec_helper.rb"
58
63
  ]
59
64
 
@@ -61,7 +66,7 @@ http://reductivelabs.com/products/puppet/
61
66
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
67
  s.specification_version = 3
63
68
 
64
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
70
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
66
71
  s.add_development_dependency(%q<yard>, [">= 0"])
67
72
  else
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'blender/cli/init'
3
+
4
+ describe Blender::Cli::Init do
5
+ before(:each) do
6
+ @init = Blender::Cli::Init.new []
7
+ end
8
+
9
+ describe :parse_options do
10
+ it "should throw usage on --help" do
11
+ proc {
12
+ @init.parse_options(%w/-h/)
13
+ }.should raise_error(RuntimeError, /\AUsage:/)
14
+
15
+ proc {
16
+ @init.parse_options(%w/--help/)
17
+ }.should raise_error(RuntimeError, <<-USAGE)
18
+ Usage: blender init [OPTIONS] HOST
19
+
20
+ Common options:
21
+ -u, --upstream-gems don't use the system gems, download and install upstream version instead
22
+ -N, --node NODE force NODE as the current nodename
23
+ -t, --trace dump trace to the stdout
24
+ -H, --hostname HOSTNAME set HOSTNAME
25
+ -h, --help Show this message
26
+ USAGE
27
+ end
28
+
29
+ it "should throw usage on missing parameters" do
30
+ proc {
31
+ @init.parse_options(%w//)
32
+ }.should raise_error(RuntimeError, /\Aplease provide a hostname/)
33
+ end
34
+
35
+ it "should throw usage on extra args" do
36
+ proc {
37
+ @init.parse_options(%w/aaa bbb/)
38
+ }.should raise_error(RuntimeError, /\Aunexpected: bbb\nUsage:/)
39
+ end
40
+
41
+ it "should parse host" do
42
+ opts = @init.parse_options(%w/aaa/)
43
+ opts[:host].should == "aaa"
44
+ end
45
+
46
+ it "should parse -u" do
47
+ @init.parse_options(%w/-u host/)[:system_gems].should == 'n'
48
+ end
49
+
50
+ it "should parse -N" do
51
+ @init.parse_options(%w/-N node1 host/)[:node].should == "node1"
52
+ end
53
+
54
+ it "should parse -t" do
55
+ @init.parse_options(%w/-t host/)[:trace].should == true
56
+ end
57
+
58
+ it "should parse -H" do
59
+ @init.parse_options(%w/-H hostname host/)[:hostname].should == "hostname"
60
+ end
61
+
62
+ end
63
+
64
+ describe :bootstrap do
65
+ it "should raise if mix fails" do
66
+ mock(File).expand_path("files/bootstrap.sh", Blender::ROOT) {"path/to/files/bootstrap.sh"}
67
+ mock(@init).run("cat path/to/files/bootstrap.sh | ssh host USE_SYSTEM_GEMS= TRACE=1 HOSTNAME=foobar.com NODE=zoo /bin/bash -eu") {false}
68
+ proc {
69
+ @init.bootstrap :trace => true, :hostname => "foobar.com", :node => "zoo", :host => "host"
70
+ }.should raise_error(RuntimeError, "failed bootstrap.sh")
71
+ end
72
+
73
+ it "should run bootstrap and return true" do
74
+ mock(File).expand_path("files/bootstrap.sh", Blender::ROOT) {"path/to/files/bootstrap.sh"}
75
+ mock(@init).run("cat path/to/files/bootstrap.sh | ssh host USE_SYSTEM_GEMS= TRACE=1 HOSTNAME=foobar.com NODE=zoo /bin/bash -eu") {true}
76
+ @init.bootstrap(:trace => true, :hostname => "foobar.com", :node => "zoo", :host => "host").should == true
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+ require 'blender/cli/mix'
3
+
4
+ describe Blender::Cli::Mix do
5
+ before(:each) do
6
+ @mix = Blender::Cli::Mix.new []
7
+ end
8
+
9
+ describe :parse_options do
10
+ it "should throw usage on --help" do
11
+ proc {
12
+ @mix.parse_options(%w/-h/)
13
+ }.should raise_error(RuntimeError, <<-USAGE)
14
+ Usage: blender mix [OPTIONS] [DIR] HOST
15
+ Options:
16
+ -r, --recipe RECIPE if RECIPE is not specified blender will first look for <directory_name>.rb and then for blender-recipe.rb
17
+ -N, --node NODE force NODE as the current nodename
18
+ -R, --roles ROLES comma delimited list of roles that should execute
19
+
20
+ Common options:
21
+ -h, --help Show this message
22
+
23
+ Notes:
24
+ "." used if DIR not specified
25
+ USAGE
26
+
27
+ proc {
28
+ @mix.parse_options(%w/--help/)
29
+ }.should raise_error(RuntimeError, /\AUsage:/)
30
+ end
31
+
32
+ it "should throw usage on missing parameters" do
33
+ proc {
34
+ @mix.parse_options(%w//)
35
+ }.should raise_error(RuntimeError, /\AUsage:/)
36
+ end
37
+
38
+ it "should throw usage on extra args" do
39
+ proc {
40
+ @mix.parse_options(%w/aaa bbb ccc/)
41
+ }.should raise_error(RuntimeError, /\Aunexpected: ccc\nUsage:/)
42
+ end
43
+
44
+ it "should only arg as host and dir as ." do
45
+ opts = @mix.parse_options(%w/aaa/)
46
+ opts[:host].should == "aaa"
47
+ opts[:dir].should == "."
48
+ end
49
+
50
+ it "should throw usage if dir doesn't exist" do
51
+ proc {
52
+ @mix.parse_options(%w/aaa bbb/)
53
+ }.should raise_error(RuntimeError, /\Aaaa is not a directory/)
54
+ end
55
+
56
+ it "should parse host and dir args" do
57
+ opts = @mix.parse_options(%w/spec some-host/)
58
+ opts[:dir].should == "spec"
59
+ opts[:host].should == "some-host"
60
+ end
61
+
62
+ it "should parse -N" do
63
+ @mix.parse_options(%w/-N node1 host/)[:node].should == "node1"
64
+ end
65
+
66
+ it "should parse -R" do
67
+ @mix.parse_options(%w/-R role1 host/)[:roles].should == "role1"
68
+ end
69
+
70
+ it "should parse -r" do
71
+ @mix.parse_options(%w/-r foo host/)[:recipe].should == "foo"
72
+ end
73
+ end
74
+
75
+ describe :find_recipe do
76
+ it "should return recipe if exists" do
77
+ mock(File).file?("path/to/foo/bar") {true}
78
+ @mix.find_recipe(:dir => "path/to/foo", :recipe => "bar").should == "bar"
79
+ end
80
+
81
+ it "should return recipe.rb if exists" do
82
+ mock(File).file?("path/to/foo/bar") {false}
83
+ mock(File).file?("path/to/foo/bar.rb") {true}
84
+ @mix.find_recipe(:dir => "path/to/foo", :recipe => "bar").should == "bar.rb"
85
+ end
86
+
87
+ it "should return dirname.rb if exists" do
88
+ mock(File).file?("path/to/foo/foo.rb") {true}
89
+ @mix.find_recipe(:dir => "path/to/foo").should == "foo.rb"
90
+ end
91
+
92
+ it "should return blender-recipe.rb if exists" do
93
+ mock(File).file?("path/to/foo/foo.rb") {false}
94
+ mock(File).file?("path/to/foo/blender-recipe.rb") {true}
95
+ @mix.find_recipe(:dir => "path/to/foo").should == "blender-recipe.rb"
96
+ end
97
+
98
+ it "should return raise error if no recipe found" do
99
+ proc {
100
+ @mix.find_recipe(:dir => "path/to/foo", :usage => "Usage:")
101
+ }.should raise_error(
102
+ RuntimeError,
103
+ /recipe not found \(looking for foo.rb blender-recipe.rb\)\nUsage:/)
104
+ end
105
+
106
+ it "should NOT look for directory.rb if given recipe" do
107
+ mock(File).file?("path/to/foo/bar") {false}
108
+ mock(File).file?("path/to/foo/bar.rb") {false}
109
+ dont_allow(File).file?("path/to/foo/foo.rb") {false}
110
+ dont_allow(File).file?("path/to/foo/blender-recipe.rb") {false}
111
+ proc {
112
+ @mix.find_recipe(:dir => "path/to/foo", :recipe => "bar")
113
+ }.should raise_error(RuntimeError, /recipe not found \(looking for bar bar.rb\)/)
114
+ end
115
+
116
+ end
117
+
118
+ describe :run_recipe do
119
+ it "should raise if init.sh fails" do
120
+ mock(File).expand_path("files/init.sh", Blender::ROOT) {"path/to/files/init.sh"}
121
+ mock(@mix).run("cat path/to/files/init.sh | ssh host /bin/bash -l") {false}
122
+ proc {
123
+ @mix.run_recipe "foo.rb", :host => "host"
124
+ }.should raise_error(RuntimeError, "failed init.sh")
125
+ end
126
+
127
+ it "should raise if rsync fails" do
128
+ stub(File).expand_path("files/init.sh", Blender::ROOT) {"path/to/files/init.sh"}
129
+ stub(@mix).run("cat path/to/files/init.sh | ssh host /bin/bash -l") {true}
130
+ mock(@mix).run("rsync -qazP --delete --exclude '.*' foo/ host:/var/lib/blender/recipes") {false}
131
+ proc {
132
+ @mix.run_recipe "foo.rb", :host => "host", :dir => "foo"
133
+ }.should raise_error(RuntimeError, "failed rsync")
134
+ end
135
+
136
+ it "should raise if mix fails" do
137
+ stub(File).expand_path("files/init.sh", Blender::ROOT) {"path/to/files/init.sh"}
138
+ stub(@mix).run("cat path/to/files/init.sh | ssh host /bin/bash -l") {true}
139
+ stub(@mix).run("rsync -qazP --delete --exclude '.*' foo/ host:/var/lib/blender/recipes") {true}
140
+ mock(File).expand_path("files/mix.sh", Blender::ROOT) {"path/to/files/mix.sh"}
141
+ mock(@mix).run("cat path/to/files/mix.sh | ssh host RECIPE=foo.rb NODE=zoo ROLES=a,b /bin/bash -l") {false}
142
+ proc {
143
+ @mix.run_recipe "foo.rb", :host => "host", :dir => "foo", :roles => "a,b", :node => "zoo"
144
+ }.should raise_error(RuntimeError, "failed mix.sh")
145
+ end
146
+
147
+ it "should run init, rsync and mix and return true" do
148
+ mock(File).expand_path("files/init.sh", Blender::ROOT) {"path/to/files/init.sh"}
149
+ mock(@mix).run("cat path/to/files/init.sh | ssh host /bin/bash -l") {true}
150
+ mock(@mix).run("rsync -qazP --delete --exclude '.*' foo/ host:/var/lib/blender/recipes") {true}
151
+ mock(File).expand_path("files/mix.sh", Blender::ROOT) {"path/to/files/mix.sh"}
152
+ mock(@mix).run("cat path/to/files/mix.sh | ssh host RECIPE=foo.rb NODE=zoo ROLES=a,b /bin/bash -l") {true}
153
+ @mix.run_recipe("foo.rb", :host => "host", :dir => "foo", :roles => "a,b", :node => "zoo").should == true
154
+ end
155
+ end
156
+
157
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+ require 'blender/cli/start'
3
+
4
+ describe Blender::Cli::Start do
5
+ before(:each) do
6
+ @start = Blender::Cli::Start.new []
7
+ end
8
+
9
+ describe :parse_options do
10
+ it "should throw usage on --help" do
11
+ proc {
12
+ @start.parse_options(%w/-h/)
13
+ }.should raise_error(RuntimeError, /\AUsage:/)
14
+
15
+ proc {
16
+ @start.parse_options(%w/--help/)
17
+ }.should raise_error(RuntimeError, <<-USAGE)
18
+ Usage: blender start [OPTIONS] [-- [ec2run options]]
19
+ Options:
20
+ -a, --ami AMI use specified AMI instead of the default one.
21
+ If you don't specify your own AMI blender will choose a defaule one:
22
+ * ami-bb709dd2 for 32 bits
23
+ * ami-55739e3c for 64 bits
24
+ You can change the defaults by writing your own AMIs
25
+ into ~/.blender/ami and ~/.blender/ami64 files
26
+
27
+ -k, --key KEY use KEY when starting instance. KEY should already be generated.
28
+ If you don't specify a KEY blender will try to use the key from your EC2 account
29
+ Note: There must be only ONE key on the account for it to work.
30
+
31
+ --64 use 64 bit default AMI. This does nothing if you specify your own AMI
32
+ -n, --dry-run Don't do anything, just print the command line to be executed
33
+
34
+ Common options:
35
+ -h, --help Show this message
36
+
37
+ Example:
38
+
39
+ # start a 64bit instance with default options
40
+ blender start -64
41
+
42
+ # start with a custom ami
43
+ blender start --ami ami-2d4aa444
44
+
45
+ # start with passing arguments to ec2run: use security group default+test
46
+ blender start -- -g default -g test
47
+ USAGE
48
+ end
49
+
50
+ it "should not fail with no parameters" do
51
+ proc {
52
+ @start.parse_options(%w//)
53
+ }.should_not raise_error
54
+ end
55
+
56
+ it "should throw usage on extra args" do
57
+ proc {
58
+ @start.parse_options(%w/aaa/)
59
+ }.should raise_error(RuntimeError, /\Aunexpected: aaa\nUsage:/)
60
+ end
61
+
62
+ it "should parse -a" do
63
+ @start.parse_options(%w/-a ami123/)[:ami].should == "ami123"
64
+ end
65
+
66
+ it "should parse -k" do
67
+ @start.parse_options(%w/-k mykey/)[:key].should == "mykey"
68
+ end
69
+
70
+ it "should parse --64" do
71
+ @start.parse_options(%w/--64/)[64].should == true
72
+ end
73
+
74
+ it "should parse -n" do
75
+ @start.parse_options(%w/-n/)[:dry].should == true
76
+ end
77
+
78
+ end
79
+
80
+ describe :default_ami do
81
+ it "should read ~/.blender/ami for 32 bit" do
82
+ mock(File).expand_path("~/.blender/ami") {"/path/to/home/.blender/ami"}
83
+ mock(File).read("/path/to/home/.blender/ami") {"foo"}
84
+ @start.default_ami.should == "foo"
85
+ end
86
+
87
+ it "should read ~/.blender/ami64 for 64 bit" do
88
+ mock(File).expand_path("~/.blender/ami64") {"/path/to/home/.blender/ami64"}
89
+ mock(File).read("/path/to/home/.blender/ami64") {"foo"}
90
+ @start.default_ami(64 => true).should == "foo"
91
+ end
92
+
93
+ it "should return AMI_32 when file fails to read" do
94
+ mock(File).expand_path("~/.blender/ami") {"/path/to/home/.blender/ami"}
95
+ mock(File).read("/path/to/home/.blender/ami") {raise "boom"}
96
+ @start.default_ami.should == Blender::Cli::Start::AMI_32
97
+ end
98
+
99
+ it "should return AMI_32 when file is empty" do
100
+ mock(File).expand_path("~/.blender/ami") {"/path/to/home/.blender/ami"}
101
+ mock(File).read("/path/to/home/.blender/ami") {" "}
102
+ @start.default_ami.should == Blender::Cli::Start::AMI_32
103
+ end
104
+ end
105
+
106
+ describe :default_key do
107
+ it "should fail if more then 1 key found" do
108
+ mock(@start).__double_definition_create__.call(:`, "ec2dkey") {"aaa\nbbb"}
109
+
110
+ proc {
111
+ @start.default_key
112
+ }.should raise_error(RuntimeError, "too many keys")
113
+ end
114
+
115
+ it "should fail if no keys found" do
116
+ mock(@start).__double_definition_create__.call(:`, "ec2dkey") {""}
117
+
118
+ proc {
119
+ @start.default_key
120
+ }.should raise_error(RuntimeError, "can't find any keys")
121
+ end
122
+
123
+ it "should parse and return single key" do
124
+ mock(@start).__double_definition_create__.call(:`, "ec2dkey") {"KEYPAIR keyname 12:34:56:78:90"}
125
+ @start.default_key.should == "keyname"
126
+ end
127
+
128
+ end
129
+
130
+ describe :start_ami do
131
+ it "should start ami with default params" do
132
+ mock(@start).default_ami(anything) {"def-ami"}
133
+ mock(@start).default_key {"def-key"}
134
+ mock(@start).run(*%w"ec2run def-ami -k def-key") {true}
135
+ @start.start_ami.should be_true
136
+ end
137
+
138
+ it "should raise error if run fails" do
139
+ mock(@start).default_ami(anything) {"def-ami"}
140
+ mock(@start).default_key {"def-key"}
141
+ mock(@start).run(*%w"ec2run def-ami -k def-key") {false}
142
+ proc {
143
+ @start.start_ami.should be_true
144
+ }.should raise_error(RuntimeError, "failed to start ami")
145
+ end
146
+ end
147
+
148
+
149
+
150
+
151
+
152
+ #describe :bootstrap do
153
+ #it "should raise if mix fails" do
154
+ #mock(File).expand_path("files/bootstrap.sh", Blender::ROOT) {"path/to/files/bootstrap.sh"}
155
+ #mock(@start).run("cat path/to/files/bootstrap.sh | ssh host USE_SYSTEM_GEMS= TRACE=1 HOSTNAME=foobar.com NODE=zoo /bin/bash -eu") {false}
156
+ #proc {
157
+ #@start.bootstrap :trace => true, :hostname => "foobar.com", :node => "zoo", :host => "host"
158
+ #}.should raise_error(RuntimeError, "failed bootstrap.sh")
159
+ #end
160
+
161
+ #it "should run bootstrap and return true" do
162
+ #mock(File).expand_path("files/bootstrap.sh", Blender::ROOT) {"path/to/files/bootstrap.sh"}
163
+ #mock(@start).run("cat path/to/files/bootstrap.sh | ssh host USE_SYSTEM_GEMS= TRACE=1 HOSTNAME=foobar.com NODE=zoo /bin/bash -eu") {true}
164
+ #@start.bootstrap(:trace => true, :hostname => "foobar.com", :node => "zoo", :host => "host").should == true
165
+ #end
166
+ #end
167
+
168
+ end
data/spec/spec.opts CHANGED
@@ -1 +1,5 @@
1
- --color
1
+ --colour
2
+ --format nested
3
+ --format profile:log/spec-benchmark.log
4
+ --loadby mtime
5
+ --reverse
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,13 @@
1
+ require 'rubygems'
2
+ require 'ruby-debug'
1
3
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
4
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'server-blender'
5
+ require 'blender'
4
6
  require 'spec'
5
7
  require 'spec/autorun'
6
8
 
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
7
11
  Spec::Runner.configure do |config|
8
-
12
+ config.mock_with :rr
9
13
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: server-blender
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 1
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 0
8
- - 14
9
- version: 0.0.14
9
+ - 15
10
+ version: 0.0.15
10
11
  platform: ruby
11
12
  authors:
12
13
  - Vitaly Kushner
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-04 00:00:00 +03:00
18
+ date: 2010-11-28 00:00:00 +02:00
18
19
  default_executable: blender
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: rspec
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 13
27
30
  segments:
28
31
  - 1
29
32
  - 2
@@ -35,9 +38,11 @@ dependencies:
35
38
  name: yard
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ">="
40
44
  - !ruby/object:Gem::Version
45
+ hash: 3
41
46
  segments:
42
47
  - 0
43
48
  version: "0"
@@ -74,11 +79,14 @@ files:
74
79
  - files/init.sh
75
80
  - files/mix.sh
76
81
  - lib/blender.rb
82
+ - lib/blender/cli.rb
77
83
  - lib/blender/cli/init.rb
78
84
  - lib/blender/cli/mix.rb
79
85
  - lib/blender/cli/start.rb
80
86
  - server-blender.gemspec
81
- - spec/server-blender_spec.rb
87
+ - spec/cli/init_spec.rb
88
+ - spec/cli/mix_spec.rb
89
+ - spec/cli/start_spec.rb
82
90
  - spec/spec.opts
83
91
  - spec/spec_helper.rb
84
92
  has_rdoc: true
@@ -91,26 +99,32 @@ rdoc_options:
91
99
  require_paths:
92
100
  - lib
93
101
  required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
94
103
  requirements:
95
104
  - - ">="
96
105
  - !ruby/object:Gem::Version
106
+ hash: 3
97
107
  segments:
98
108
  - 0
99
109
  version: "0"
100
110
  required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
101
112
  requirements:
102
113
  - - ">="
103
114
  - !ruby/object:Gem::Version
115
+ hash: 3
104
116
  segments:
105
117
  - 0
106
118
  version: "0"
107
119
  requirements: []
108
120
 
109
121
  rubyforge_project:
110
- rubygems_version: 1.3.6
122
+ rubygems_version: 1.3.7
111
123
  signing_key:
112
124
  specification_version: 3
113
125
  summary: Server provisioning and configuration management tool
114
126
  test_files:
115
- - spec/server-blender_spec.rb
127
+ - spec/cli/init_spec.rb
128
+ - spec/cli/mix_spec.rb
129
+ - spec/cli/start_spec.rb
116
130
  - spec/spec_helper.rb
@@ -1,7 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "ServerBlender" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
6
- end
7
- end