skewer 0.1.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.
Files changed (81) hide show
  1. data/.idea/encodings.xml +5 -0
  2. data/.idea/misc.xml +8 -0
  3. data/.idea/modules.xml +9 -0
  4. data/.idea/skewer.iml +61 -0
  5. data/.idea/vcs.xml +7 -0
  6. data/.rvmrc +2 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +146 -0
  9. data/LICENSE +13 -0
  10. data/README.md +77 -0
  11. data/Rakefile +59 -0
  12. data/Vagrantfile +5 -0
  13. data/assets/Gemfile +5 -0
  14. data/assets/rubygems.sh +39 -0
  15. data/bin/skewer +101 -0
  16. data/features/aws.feature +45 -0
  17. data/features/bootstrapper.feature +14 -0
  18. data/features/configuration.feature +15 -0
  19. data/features/delete.feature +22 -0
  20. data/features/hooks.feature +15 -0
  21. data/features/interface.feature +61 -0
  22. data/features/provision.feature +38 -0
  23. data/features/rackspace.feature +34 -0
  24. data/features/step_definitions/cucumber_steps.rb +43 -0
  25. data/features/step_definitions/delete.rb +32 -0
  26. data/features/support/env.rb +6 -0
  27. data/features/support/puppetcode/manifests/classes/foobar.pp +4 -0
  28. data/features/support/puppetcode/manifests/classes/foobroken.pp +4 -0
  29. data/features/support/puppetcode/manifests/classes/role.pp +3 -0
  30. data/features/support/puppetcode/manifests/site.pp +2 -0
  31. data/features/support/puppetcode/modules/foo/manifests/bar.pp +3 -0
  32. data/features/support/puppetcode/modules/foo/manifests/broken.pp +8 -0
  33. data/features/support/puppetcode/modules/puppet/manifests/munge.pp +5 -0
  34. data/features/update.feature +29 -0
  35. data/lib/aws/node.rb +52 -0
  36. data/lib/aws/security_group.rb +37 -0
  37. data/lib/aws/service.rb +21 -0
  38. data/lib/bootstrapper.rb +112 -0
  39. data/lib/cli.rb +96 -0
  40. data/lib/config.rb +67 -0
  41. data/lib/cuke.rb +26 -0
  42. data/lib/ersatz/ersatz_node.rb +31 -0
  43. data/lib/ersatz/ssh_result.rb +15 -0
  44. data/lib/eucalyptus.rb +15 -0
  45. data/lib/hooks.rb +22 -0
  46. data/lib/node.erb +5 -0
  47. data/lib/node.rb +43 -0
  48. data/lib/parser.rb +92 -0
  49. data/lib/puppet.rb +53 -0
  50. data/lib/puppet_node.rb +26 -0
  51. data/lib/puppet_runtime_error.rb +6 -0
  52. data/lib/rackspace/images.rb +25 -0
  53. data/lib/rackspace/node.rb +60 -0
  54. data/lib/skewer.rb +13 -0
  55. data/lib/skewer/version.rb +3 -0
  56. data/lib/source.rb +54 -0
  57. data/lib/stub_node.rb +25 -0
  58. data/lib/util.rb +17 -0
  59. data/skewer.gemspec +30 -0
  60. data/spec/aws/node_spec.rb +70 -0
  61. data/spec/aws/security_group_spec.rb +20 -0
  62. data/spec/aws/service_spec.rb +31 -0
  63. data/spec/bootstrapper_spec.rb +116 -0
  64. data/spec/cli_spec.rb +71 -0
  65. data/spec/config_spec.rb +68 -0
  66. data/spec/cuke_spec.rb +46 -0
  67. data/spec/ersatz_node_spec.rb +9 -0
  68. data/spec/ersatz_ssh_result_spec.rb +16 -0
  69. data/spec/hooks_spec.rb +19 -0
  70. data/spec/logger_spec.rb +22 -0
  71. data/spec/parser_spec.rb +93 -0
  72. data/spec/puppet.rb +47 -0
  73. data/spec/puppet_node_spec.rb +31 -0
  74. data/spec/rackspace/images_spec.rb +37 -0
  75. data/spec/rackspace/node_spec.rb +30 -0
  76. data/spec/source_spec.rb +45 -0
  77. data/spec/spec_helper.rb +10 -0
  78. data/spec/support/features/example.feature +9 -0
  79. data/spec/support/features/step_definitions/example.rb +8 -0
  80. data/spec/util_spec.rb +27 -0
  81. metadata +288 -0
@@ -0,0 +1,68 @@
1
+ require 'rspec'
2
+ require 'config'
3
+
4
+ describe Skewer::SkewerConfig do
5
+ before(:each) do
6
+ config = Skewer::SkewerConfig.instance
7
+ config.reset
8
+ end
9
+
10
+ it "should only have one instance" do
11
+ config1 = Skewer::SkewerConfig.instance
12
+ config2 = Skewer::SkewerConfig.instance
13
+ config1.should == config2
14
+ end
15
+
16
+ it "should have some default options" do
17
+ config = Skewer::SkewerConfig.instance
18
+ config.get(:puppet_repo).should == '../infrastructure'
19
+ config.get(:region).should == 'us-east-1'
20
+ config.get(:flavor_id).should == 'm1.large'
21
+ end
22
+
23
+ it "should parse json" do
24
+ config = Skewer::SkewerConfig.instance
25
+ config.parse('{"seat_number": "29C"}')
26
+ config.get(:seat_number).should == '29C'
27
+ end
28
+
29
+ it "should parse json and over-ride the defaults" do
30
+ config = Skewer::SkewerConfig.instance
31
+ config.parse('{"fuel_type": "Unleaded", "puppet_repo": "/tmp/codez"}')
32
+ config.get(:fuel_type).should == 'Unleaded'
33
+ config.get(:puppet_repo).should == '/tmp/codez'
34
+ end
35
+
36
+ it "should have consistent getters and setters" do
37
+ config = Skewer::SkewerConfig.instance
38
+ config.set(:test_key, 'test_value')
39
+ config.get(:test_key).should == 'test_value'
40
+ end
41
+
42
+ it "should be able to swallow options" do
43
+ config = Skewer::SkewerConfig.instance
44
+ options = {'rodent' => 'bunnyrabbit', 'canine' => 'dingo' }
45
+ config.slurp_options(options)
46
+ config.get('rodent').should == 'bunnyrabbit'
47
+ config.get('canine').should == 'dingo'
48
+ config.get('region').should == 'us-east-1'
49
+ end
50
+
51
+ it "should take the region as a parameter" do
52
+ config = Skewer::SkewerConfig.instance
53
+ config.set(:region, 'unknown region')
54
+ config.get(:region).should == 'unknown region'
55
+ end
56
+
57
+ it "should have the region available as an attribute when slurped" do
58
+ config = Skewer::SkewerConfig.instance
59
+ config.region.should == 'us-east-1'
60
+ config.slurp_options({:region => 'eu-west-1'})
61
+ config.region.should == 'eu-west-1'
62
+ end
63
+
64
+ it "should have a region accessor" do
65
+ config = Skewer::SkewerConfig.instance
66
+ config.region.should == 'us-east-1'
67
+ end
68
+ end
data/spec/cuke_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'rspec'
2
+ require 'cuke'
3
+
4
+ describe Skewer::Cuke do
5
+ it "should only accept valid directories for construction" do
6
+ lambda {
7
+ Skewer::Cuke.new(nil)
8
+ }.should raise_exception(RuntimeError, "you must provide a valid directory for features to be executed within")
9
+
10
+ lambda {
11
+ Skewer::Cuke.new(123)
12
+ }.should raise_exception(RuntimeError, "you must provide a valid directory for features to be executed within")
13
+
14
+ lambda {
15
+ Skewer::Cuke.new('')
16
+ }.should raise_exception(RuntimeError, "you must provide a valid directory for features to be executed within")
17
+
18
+ lambda {
19
+ Skewer::Cuke.new(123)
20
+ }.should raise_exception(RuntimeError, "you must provide a valid directory for features to be executed within")
21
+
22
+ lambda {
23
+ Skewer::Cuke.new('./spec/dir_that_doenst_exist')
24
+ }.should raise_exception(RuntimeError, "you must provide a valid directory for features to be executed within")
25
+ end
26
+
27
+ it "should build out the object if given a valid directory" do
28
+ cuke = Skewer::Cuke.new('./spec')
29
+ cuke.nil?.should == false
30
+ cuke.class.should == Skewer::Cuke
31
+ end
32
+
33
+ it "should successfully run cucumber within the provided directory" do
34
+ cuke = Skewer::Cuke.new('./target')
35
+ result = cuke.run
36
+ result = result.match(/failure/)[0] rescue false
37
+ result.should == false
38
+ end
39
+
40
+ it "should throw an exception if cucumber finds a failing feature" do
41
+ cuke = Skewer::Cuke.new('./spec/support/features')
42
+ lambda {
43
+ cuke.run
44
+ }.should raise_exception(Skewer::Cuke::CukeError)
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ require 'lib/ersatz/ersatz_node.rb'
2
+
3
+ describe Skewer::ErsatzNode do
4
+ it "should have attributes" do
5
+ node = Skewer::ErsatzNode.new('mehostname','meuser')
6
+ node.username.should == 'meuser'
7
+ node.dns_name.should == 'mehostname'
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ require 'ersatz/ssh_result'
2
+
3
+ describe Skewer::ErsatzSSHResult do
4
+ it "should build out an SSH object" do
5
+ e = Skewer::ErsatzSSHResult.new('cmd', 'stdout', 'status')
6
+ e.nil?.should == false
7
+ e.class.should == Skewer::ErsatzSSHResult
8
+ end
9
+
10
+ it "should have attribute accessors" do
11
+ e = Skewer::ErsatzSSHResult.new('cmd', 'stdout', 'status')
12
+ e.command.should == 'cmd'
13
+ e.stdout.should == 'stdout'
14
+ e.status.should == 'status'
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ require "rspec"
2
+ require 'hooks'
3
+
4
+ describe "Hooks" do
5
+
6
+ it "should quietly go off and execute something" do
7
+
8
+ hooks = Skewer::Hooks.new( 'foobar.build-doctor.com')
9
+ hooks.command = 'echo doodoo'
10
+ hooks.run.should == true
11
+ end
12
+
13
+ it "should not bother if there are no hooks" do
14
+
15
+
16
+ hooks = Skewer::Hooks.new( 'foobar.build-doctor.com')
17
+ hooks.run.should == false
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require 'skewer'
2
+
3
+ describe Skewer do
4
+ it "should have a logger object available on the module" do
5
+ Skewer.logger.nil?.should == false
6
+ Skewer.logger.class.should == Logger
7
+ end
8
+
9
+ it "should have a way to inject a new logger" do
10
+ # Check that a Logger already exists.
11
+ Skewer.logger.nil?.should == false
12
+ Skewer.logger.class.should == Logger
13
+
14
+ # Override the logger.
15
+ Skewer.logger = String.new
16
+ Skewer.logger.nil?.should == false
17
+ Skewer.logger.class.should == String
18
+
19
+ # Return the logger back to being Logger.
20
+ Skewer.logger = Logger.new(STDOUT)
21
+ end
22
+ end
@@ -0,0 +1,93 @@
1
+ require 'parser'
2
+
3
+ USAGE = <<EOF
4
+ Usage: skewer COMMAND [options]
5
+
6
+ The available skewer commands are:
7
+ provision spawn a new VM via a cloud system and provision it with puppet code
8
+ update update the puppet code on a machine that you've already provisioned
9
+ delete deletes the given host from the provided cloud provider
10
+ EOF
11
+ USAGE.strip!
12
+
13
+ UPDATE_USAGE = "Usage: skewer update --host <host> --user <user with sudo rights> --role <puppet role class>"
14
+ PROVISION_USAGE = "Usage: skewer provision --cloud <which cloud> --image <AWS image> --role <puppet role class>"
15
+ DELETE_USAGE = "Usage: skewer delete --cloud <which cloud> --host <host>"
16
+
17
+ describe Skewer::CLI::Parser do
18
+ it "should barf if given no params" do
19
+ lambda {
20
+ parser = Skewer::CLI::Parser.new
21
+ }.should raise_exception(SystemExit, USAGE)
22
+ end
23
+
24
+ it "should only accept 'provision' and 'update' as types" do
25
+ lambda {
26
+ parser = Skewer::CLI::Parser.new('test', {:bloom => 'filter'})
27
+ }.should raise_exception(SystemExit, USAGE)
28
+ end
29
+
30
+ it "should barf if it doesnt know how to provision something odd" do
31
+ lambda {
32
+ parser = Skewer::CLI::Parser.new('provision', {:kind => "FOOBAR", :image => true, :role => true})
33
+ }.should raise_exception(RuntimeError, "I don't know that cloud")
34
+
35
+ end
36
+ it "should barf if it doesn't know how to update something odd" do
37
+ lambda {
38
+ parser = Skewer::CLI::Parser.new('update', {:host => "googoo", :user => "shoopidoop"})
39
+ }.should raise_exception(RuntimeError, "I don't know that cloud")
40
+ end
41
+
42
+ it "should raise a usage exception if using 'provision' without correct options" do
43
+ lambda {
44
+ parser = Skewer::CLI::Parser.new('provision', {:bloom => 'filter'})
45
+ }.should raise_exception(SystemExit, PROVISION_USAGE)
46
+ end
47
+
48
+ it "should raise a usage exception if using 'update' without correct options" do
49
+ lambda {
50
+ parser = Skewer::CLI::Parser.new('update', {:bloom => 'filter'})
51
+ }.should raise_exception(SystemExit, UPDATE_USAGE)
52
+ end
53
+
54
+ it "should show the main usage message if provided the help option" do
55
+ lambda {
56
+ parser = Skewer::CLI::Parser.new(false, {:help => true})
57
+ }. should raise_exception(SystemExit, USAGE)
58
+ end
59
+
60
+ it "should show the provision usage message if provided 'provision' with the help option" do
61
+ lambda {
62
+ parser = Skewer::CLI::Parser.new('provision', {:kind => true, :image => true, :role => true, :help => true})
63
+ }.should raise_exception(SystemExit, PROVISION_USAGE)
64
+ end
65
+
66
+ it "should show the update usage message if provided 'update' with the help option" do
67
+ lambda {
68
+ parser = Skewer::CLI::Parser.new('update', {:host => true, :user => true, :help => true})
69
+ }.should raise_exception(SystemExit, UPDATE_USAGE)
70
+ end
71
+
72
+ it "should require --key if cloud is AWS" do
73
+ lambda {
74
+ parser = Skewer::CLI::Parser.new('provision', {:kind => :ec2})
75
+ }.should raise_exception(SystemExit, "A key (--key KEY) must be provided if using EC2")
76
+ end
77
+
78
+ it "should show the delete usage if provided 'delete' with the help option" do
79
+ lambda {
80
+ parser = Skewer::CLI::Parser.new('delete', {:host => true, :kind => true, :help => true})
81
+ }.should raise_exception(SystemExit, DELETE_USAGE)
82
+ end
83
+
84
+ it "should show the delete usage if not given the correct inputs" do
85
+ lambda {
86
+ parser = Skewer::CLI::Parser.new('delete', {:host => true})
87
+ }.should raise_exception(SystemExit, DELETE_USAGE)
88
+
89
+ lambda {
90
+ parser = Skewer::CLI::Parser.new('delete', {:kind => true})
91
+ }.should raise_exception(SystemExit, DELETE_USAGE)
92
+ end
93
+ end
data/spec/puppet.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'lib/puppet'
2
+
3
+ describe Skewer::Puppet do
4
+
5
+ before(:each) do
6
+ @puppet = Skewer::Puppet.new
7
+ @root = File.expand_path File.join(File.dirname(__FILE__), "..")
8
+ @prefix = "cd infrastructure && /var/lib/gems/1.8/bin/bundle exec puppet apply manifests/site.pp --color false"
9
+ @sudo_prefix = "cd infrastructure && sudo /var/lib/gems/1.8/bin/bundle exec puppet apply manifests/site.pp --color false"
10
+ end
11
+
12
+ it "should sudo when connecting as root" do
13
+ @puppet.command_string('goober', {}).should == "#{@sudo_prefix} --modulepath modules --vardir /var/lib/puppet"
14
+ end
15
+
16
+ it "should not pass a certname if you give it a role" do
17
+ @puppet.command_string('goober', {:role => 'graunch'}).should == "#{@sudo_prefix} --modulepath modules --vardir /var/lib/puppet"
18
+ end
19
+
20
+ it "should not sudo when connecting as root" do
21
+ @puppet.command_string('root', {}).should == "#{@prefix} --modulepath modules --vardir /var/lib/puppet"
22
+ end
23
+
24
+ it "should pass noop when if you pass the option" do
25
+ @puppet.command_string('root', {:noop => true}).should == "#{@prefix} --modulepath modules --vardir /var/lib/puppet --noop"
26
+ end
27
+
28
+ it "should have args for our custom modulepath" do
29
+ @puppet.arguments.should match(/modulepath/)
30
+ end
31
+
32
+ it "should not have args for external node configuration" do
33
+ @puppet.arguments.should_not match(/external_nodes/)
34
+ end
35
+
36
+ it "should blow up if something fails" do
37
+ node = mock('node')
38
+ result = stub('result')
39
+ result.stub!('status').and_return(1)
40
+ result.stub!('command').and_return('sys-unconfig')
41
+ result.stub!('stdout').and_return('failure on stdout')
42
+ result.stub!('stderr').and_return('failure on stderr')
43
+ node.should_receive(:username).and_return('danmadams')
44
+ node.should_receive(:ssh).and_return([result])
45
+ lambda { @puppet.run(node, {}) }.should raise_exception Skewer::PuppetRuntimeError
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ require 'puppet_node'
2
+ require 'config'
3
+
4
+ describe Skewer::PuppetNode do
5
+ it "should have a default nodes block" do
6
+ Skewer::PuppetNode.new.to_s.should match /node default/
7
+ Skewer::PuppetNode.new.to_s.should match /include noop/
8
+ end
9
+
10
+ it "should render a node name with a class" do
11
+ pn = Skewer::PuppetNode.new({ :foobar => "cafefoobar::install" })
12
+ pn.to_s.should match /node foobar \{/
13
+ pn.to_s.should match /include cafefoobar::install/
14
+ pn.to_s.should match /\}/
15
+ end
16
+
17
+ it "should know about the puppet directory" do
18
+ nodes = {"shoopdoop" => "foopidoop"}
19
+ config = Skewer::SkewerConfig.instance
20
+ pn = Skewer::PuppetNode.new(nodes)
21
+ pn.puppet_repo.should == config.get(:puppet_repo)
22
+ end
23
+
24
+ it "should allow you to pass in a role on the fly" do
25
+ FileUtils.mkdir_p('target/manifests')
26
+ Skewer::SkewerConfig.instance.set(:puppet_repo, 'target')
27
+ pn = Skewer::PuppetNode.new({:default => :foobar})
28
+ pn.render
29
+ File.read('target/manifests/nodes.pp').should match(/foobar/)
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ require 'rspec'
2
+ require 'fog'
3
+ require 'rackspace/images'
4
+
5
+ describe Skewer::Rackspace::Images do
6
+ before(:each) do
7
+ @images = Skewer::Rackspace::Images.new
8
+ end
9
+
10
+ it "should build out an object" do
11
+ @images.nil?.should == false
12
+ end
13
+
14
+ it "should have a supported images hash associated with the hash" do
15
+ @images.supported.class.should == Hash
16
+ end
17
+
18
+ it "should provide a default image ID if bad input provided" do
19
+ @images.get_id(nil).should == 112
20
+ @images.get_id(false).should == 112
21
+ @images.get_id(true).should == 112
22
+ end
23
+
24
+ it "should raise an exception if a bad name is provided" do
25
+ lambda {
26
+ @images.get_id('bleep de bloop')
27
+ }.should raise_exception(RuntimeError)
28
+ end
29
+
30
+ it "should return the ID if the correct name is provided" do
31
+ @images.get_id('ubuntu1104').should == 115
32
+ end
33
+
34
+ it "should pass back an integer if given an integer" do
35
+ @images.get_id(2324234).should == 2324234
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ require 'rspec'
2
+ require 'rackspace/node'
3
+
4
+ describe Skewer::Rackspace::Node do
5
+ it "should build out a basic rackspace node" do
6
+ lambda {
7
+ Fog.mock!
8
+ Skewer::Rackspace::Node.new
9
+ }.should raise_exception Fog::Errors::MockNotImplemented
10
+ end
11
+
12
+ it "should be able to find the node by IP" do
13
+ service = stub('rackspace_service')
14
+ servers = stub('servers')
15
+ node = stub('node')
16
+ service.should_receive(:servers).and_return(servers)
17
+ servers.should_receive(:select).and_return(node)
18
+ node.should_receive(:size).and_return(1)
19
+ node.should_receive(:[]).and_return(true)
20
+ Skewer::Rackspace::Node.find_by_ip('192.168.0.1', service)
21
+ end
22
+
23
+ it "should have a delete method" do
24
+
25
+ node = stub('node')
26
+ node.should_receive(:delete).and_return(true)
27
+ Skewer::Rackspace::Node.new(nil, nil, nil,node).node.delete.should == true
28
+
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ require 'source'
2
+
3
+ describe Skewer::Source do
4
+ it "should have a source directory that exists" do
5
+ lambda {
6
+ Skewer::Source.new('/tmp')
7
+ }.should_not raise_exception RuntimeError
8
+
9
+ lambda {
10
+ Skewer::Source.new('/dev/boomlakkalakka/boomlakkalakka')
11
+ }.should raise_exception RuntimeError
12
+ end
13
+
14
+ it "should exclude git from the upload list" do
15
+ Skewer::Source.new('/tmp').excludes.should include '.git'
16
+ end
17
+
18
+ it "should make a parent directory to shut rsync up" do
19
+ node = mock('node')
20
+ node.should_receive(:dns_name).at_least(3).times.and_return('this.should.not.resolve')
21
+ node.should_receive(:username).at_least(3).times.and_return('jimmy')
22
+ node.should_receive(:ssh)
23
+ source = Skewer::Source.new('/tmp/')
24
+ lambda { source.rsync(node) }.should raise_exception RuntimeError
25
+ end
26
+
27
+ it "should rsync the source tree to the remote node" do
28
+ node = mock('node')
29
+ node.should_receive(:dns_name).and_return('foo.foo.com')
30
+ node.should_receive(:username).and_return('jimmy')
31
+ source = Skewer::Source.new('/tmp/')
32
+ source.rsync_command(node).should == 'rsync --exclude Gemfile --exclude Gemfile.lock --exclude .git --delete -arpze ssh /tmp/. jimmy@foo.foo.com:infrastructure/.'
33
+ end
34
+
35
+ it "should blow up if it can't connect to the remote node" do
36
+ node = stub('node')
37
+ node.stub!(:dns_name).and_return('com.foo.foo')
38
+ node.stub!(:username).and_return('jimmy')
39
+ node.should_receive(:ssh)
40
+
41
+ lambda {
42
+ Skewer::Source.new('/tmp').rsync(node)
43
+ }.should raise_exception RuntimeError
44
+ end
45
+ end