skewer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/lib/cuke.rb ADDED
@@ -0,0 +1,26 @@
1
+ module Skewer
2
+ # Runs the Cucumber features for a given directory.
3
+ #
4
+ # Specifically, the tests related to infrastructure that skewer
5
+ # will map against to check if the node has been successfully
6
+ # built.
7
+ class Cuke
8
+ class CukeError < RuntimeError; end
9
+
10
+ def initialize(dir = nil)
11
+ raise "you must provide a valid directory for features to be executed within" if dir.nil? or dir.class != String or !directory_exists?(dir)
12
+ @dir = dir
13
+ end
14
+
15
+ def directory_exists?(dir)
16
+ File.directory?(dir)
17
+ end
18
+
19
+ def run
20
+ result = `cucumber #{@dir}`
21
+ parsed = result.match(/failed/)[0] rescue false
22
+ raise CukeError, "One of the cuke features failed!\n\n#{result}" if parsed
23
+ result
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'ersatz/ssh_result.rb'
2
+
3
+ module Skewer
4
+ # fakes a fog node
5
+ class ErsatzNode
6
+ attr_accessor :username, :dns_name
7
+
8
+ def initialize(hostname, user)
9
+ @dns_name = hostname
10
+ @username = user
11
+ end
12
+
13
+ def ssh(command)
14
+ full_ssh_command = "ssh -l #{@username} #{@dns_name} '#{command}'"
15
+ Skewer.logger.debug full_ssh_command
16
+ stdout = `#{full_ssh_command}`
17
+ result = ErsatzSSHResult.new(command, stdout, $?.exitstatus)
18
+ [result]
19
+ end
20
+
21
+ def scp(file, dest)
22
+ `scp #{file} #{@username}@#{@dns_name}:#{dest}`
23
+ end
24
+
25
+ def destroy
26
+ end
27
+
28
+ def wait_for
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ require 'skewer'
2
+
3
+ module Skewer
4
+ # fakes a fog SSH result
5
+ class ErsatzSSHResult
6
+ attr_accessor :command, :stdout, :status
7
+
8
+ def initialize(command, stdout, status)
9
+ @command = command
10
+ @stdout = stdout
11
+ @status = status
12
+ Skewer.logger.debug self.stdout
13
+ end
14
+ end
15
+ end
data/lib/eucalyptus.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'fog'
3
+
4
+ module Skewer
5
+ # instantiates a eucalyptus endpoint
6
+ class Eucalyptus
7
+ def initialize
8
+ compute = Fog::Compute.new({
9
+ :provider => 'AWS',
10
+ :endpoint => 'http://localhost:4567'
11
+ })
12
+ @node = compute.servers.bootstrap()
13
+ end
14
+ end
15
+ end
data/lib/hooks.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Skewer
2
+ require 'config'
3
+
4
+ class Hooks
5
+ attr_writer :command
6
+
7
+ def initialize(host_name)
8
+ @command = SkewerConfig.instance.get('hook')
9
+ @host_name = host_name
10
+ end
11
+
12
+ def run
13
+ return_code = false
14
+ unless @command.nil?
15
+ Skewer.logger.debug "Running hooks ..."
16
+ `#{@command} #{@host_name}`
17
+ return_code = $? == 0 ? true : false
18
+ end
19
+ return_code
20
+ end
21
+ end
22
+ end
data/lib/node.erb ADDED
@@ -0,0 +1,5 @@
1
+ <% @nodes.each_pair do |k,v|%>
2
+ node <%= k %> {
3
+ include <%= v %>
4
+ }
5
+ <% end %>
data/lib/node.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Skewer
2
+ # responsible for talking to remote machines
3
+ class Node
4
+ attr_reader :username
5
+
6
+ def initialize
7
+ @address = nil
8
+ @username = nil
9
+ @password = nil
10
+ @pubkey = nil
11
+ @ssh = Fog::SSH::Real.new(
12
+ @address,
13
+ @username,
14
+ {:password => @password}
15
+ )
16
+ install_pubkey(@ssh, @pubkey)
17
+ end
18
+
19
+ def install_pubkey(ssh, key)
20
+ key_file = File.read(key).gsub("\n",'')
21
+ ssh(['mkdir -p .ssh', 'chmod 0700 .ssh',"echo #{key_file} > .ssh/authorized_keys"])
22
+ end
23
+
24
+ def dns_name
25
+ @address
26
+ end
27
+
28
+ def wait_for(*blk)
29
+ end
30
+
31
+ def destroy
32
+ end
33
+
34
+ def ssh(commands)
35
+ results = @ssh.run(commands)
36
+ if results.is_a?(Array)
37
+ results.each {|result| Skewer.logger.debug result.stdout }
38
+ else
39
+ Skewer.logger.debug results.stdout
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/parser.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'fog'
2
+ require 'cli'
3
+ require 'aws/node'
4
+ require "rackspace/node"
5
+
6
+ module Skewer
7
+ class CLI
8
+ # Parses the CLI input and makes sure that it's clean.
9
+ class Parser
10
+ def initialize(type = nil, options = {})
11
+ # base case tests that we have input that we accept.
12
+ Fog.mock! if options[:mock]
13
+ validate_options(options, type)
14
+
15
+ if type == 'delete'
16
+ case options[:kind]
17
+ when :ec2
18
+ node = AWS::Node.find_by_name(options[:host])
19
+ when :rackspace
20
+ node = Rackspace::Node.find_by_ip(options[:host])
21
+ else
22
+ raise("#{options[:kind]} not found")
23
+ end
24
+ destroy_node(node, options)
25
+ else
26
+ Skewer::CLI.bootstrap_and_go(options)
27
+ end
28
+ end
29
+
30
+ def destroy_node(node, options)
31
+ if node
32
+ node.destroy
33
+ Skewer.logger.info("#{options[:host]} deleted.")
34
+ else
35
+ abort("#{options[:host]} not found.")
36
+ end
37
+ end
38
+
39
+ def validate_options(options, type)
40
+ abort(usage) if type.nil? and options.empty?
41
+ abort(usage) unless ['provision', 'update', 'delete'].include? type
42
+ abort("A key (--key KEY) must be provided if using EC2") if options[:kind] == :ec2 && !options[:key_name]
43
+ if type == 'provision'
44
+ unless options[:kind] && options[:image] && options[:role] && !options[:help]
45
+ abort(provision_usage)
46
+ end
47
+ elsif type == 'update'
48
+ unless options[:host] && options[:user] && !options[:help]
49
+ abort(update_usage)
50
+ end
51
+ elsif type == 'delete'
52
+ unless options[:kind] && options[:host] && !options[:help]
53
+ abort(delete_usage)
54
+ end
55
+ end
56
+ end
57
+
58
+ def usage
59
+ out = <<EOF
60
+ Usage: skewer COMMAND [options]
61
+
62
+ The available skewer commands are:
63
+ provision spawn a new VM via a cloud system and provision it with puppet code
64
+ update update the puppet code on a machine that you've already provisioned
65
+ delete deletes the given host from the provided cloud provider
66
+ EOF
67
+ out.strip
68
+ end
69
+
70
+ def provision_usage
71
+ out = <<EOF
72
+ Usage: skewer provision --cloud <which cloud> --image <AWS image> --role <puppet role class>
73
+ EOF
74
+ out.strip
75
+ end
76
+
77
+ def update_usage
78
+ out = <<EOF
79
+ Usage: skewer update --host <host> --user <user with sudo rights> --role <puppet role class>
80
+ EOF
81
+ out.strip
82
+ end
83
+
84
+ def delete_usage
85
+ out = <<EOF
86
+ Usage: skewer delete --cloud <which cloud> --host <host>
87
+ EOF
88
+ out.strip
89
+ end
90
+ end
91
+ end
92
+ end
data/lib/puppet.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'skewer'
2
+
3
+ module Skewer
4
+ require 'puppet_runtime_error'
5
+ # responsible for executing puppet
6
+ class Puppet
7
+ def arguments
8
+ [
9
+ "--modulepath modules",
10
+ "--vardir /var/lib/puppet"
11
+ ].join(' ')
12
+ end
13
+
14
+ def bundle
15
+ "/var/lib/gems/1.8/bin/bundle"
16
+ end
17
+
18
+ def command_string(username, options)
19
+ @command_line = "cd infrastructure"
20
+ if username == 'root'
21
+ @command_line << " &&"
22
+ else
23
+ @command_line << " && sudo"
24
+ end
25
+ @command_line << " #{self.bundle} exec"
26
+ @command_line << " puppet apply"
27
+ @command_line << " manifests/site.pp"
28
+ @command_line << " --color false"
29
+ @command_line << " #{arguments}"
30
+ if options[:noop]
31
+ @command_line << " --noop"
32
+ end
33
+ @command_line
34
+ end
35
+
36
+ def run(node, options)
37
+ command = command_string(node.username, options)
38
+ result = node.ssh(command)[0]
39
+ if result.status != 0
40
+ Skewer.logger.debug result.stdout
41
+ raise PuppetRuntimeError, "Puppet failed"
42
+ else
43
+ Skewer.logger.debug "Puppet run succeeded"
44
+ end
45
+ result
46
+ end
47
+
48
+ def self.run(node, options)
49
+ this = self.new
50
+ this.run(node, options)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,26 @@
1
+ module Skewer
2
+ # responsible for creating a node definition
3
+ class PuppetNode
4
+ attr_accessor :nodes, :puppet_repo
5
+
6
+ def initialize(nodes = { :default =>:noop})
7
+ @nodes = nodes
8
+ config = SkewerConfig.instance
9
+ @puppet_repo = config.get(:puppet_repo)
10
+ end
11
+
12
+ def to_s
13
+ require 'erb'
14
+ lib_dir = File.dirname(__FILE__)
15
+ template = ERB.new(File.read(File.join(lib_dir, 'node.erb')))
16
+ template.result(binding)
17
+ end
18
+
19
+ def render
20
+ node_file_path = File.join(@puppet_repo, 'manifests','nodes.pp')
21
+ file = File.new(node_file_path, 'w+')
22
+ file << self.to_s
23
+ file.close
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ module Skewer
2
+ class PuppetRuntimeError < Exception
3
+
4
+ end
5
+
6
+ end
@@ -0,0 +1,25 @@
1
+ module Skewer
2
+ module Rackspace
3
+ class Images
4
+ attr_reader :supported
5
+
6
+ def initialize
7
+ @supported = {
8
+ 'ubuntu1004' => { :id => 112, :name => "Ubuntu 10.04 LTS"},
9
+ 'ubuntu1104' => { :id => 115, :name => "Ubuntu 11.04"},
10
+ 'ubuntu1110' => { :id => 119, :name => "Ubuntu 11.10"},
11
+ }
12
+ end
13
+
14
+ # If provided a name for an image, give back the ID.
15
+ def get_id(name)
16
+ return @supported['ubuntu1004'][:id] if name.nil?
17
+ name = Integer(name) rescue name
18
+ return name if name.class == Fixnum
19
+ return @supported['ubuntu1004'][:id] if name.class != String
20
+ raise "An image with the name '#{name}' doesn't exist" if !@supported.has_key?(name)
21
+ @supported[name][:id]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,60 @@
1
+ require 'fog'
2
+ require 'rackspace/images'
3
+
4
+ module Skewer
5
+ module Rackspace
6
+ # Build out a Rackspace node using Fog.
7
+ class Node
8
+ attr_reader :node
9
+
10
+ # By default, boot an Ubuntu 10.04 LTS (lucid) server.
11
+ def initialize(flavor = 1, image = 112, name = 'my_server', instance = nil)
12
+ connection = self.class.find_service
13
+
14
+ # Get our SSH key to attach it to the server.
15
+ if instance
16
+ @node = instance
17
+ else
18
+ @node = build(connection, flavor, image, name)
19
+ end
20
+ end
21
+
22
+ def self.find_service
23
+ Fog::Compute.new(
24
+ :provider => 'Rackspace',
25
+ :rackspace_api_key => Fog.credentials[:rackspace_api_key],
26
+ :rackspace_username => Fog.credentials[:rackspace_username],
27
+ :rackspace_auth_url => "lon.auth.api.rackspacecloud.com")
28
+ end
29
+
30
+ def build(connection, flavor, image, name)
31
+ path = File.expand_path '~/.ssh/id_rsa.pub'
32
+ path = File.expand_path '~/.ssh/id_dsa.pub' if not File.exist? path
33
+ raise "Couldn't find a public key" if not File.exist? path
34
+ key = File.open(path, 'rb').read
35
+
36
+ images = Rackspace::Images.new
37
+ options = {
38
+ :flavor_id => flavor,
39
+ :image_id => images.get_id(image),
40
+ :name => name,
41
+ :public_key => key
42
+ }
43
+ connection.servers.bootstrap(options)
44
+ end
45
+
46
+ def destroy
47
+ @node.destroy if !@node.nil?
48
+ end
49
+
50
+ def self.find_by_ip(ip_address, service = self.find_service())
51
+ node = service.servers.select { |server| server.public_ip_address == ip_address }
52
+ if node.size > 0
53
+ return self.new(nil, nil, nil, node[0])
54
+ else
55
+ return false
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/skewer.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Skewer
2
+ require 'logger'
3
+
4
+ class << self
5
+ def logger
6
+ @log = @log ? @log : Logger.new(STDOUT)
7
+ end
8
+
9
+ def logger=(logger)
10
+ @log = logger
11
+ end
12
+ end
13
+ end