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
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