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.
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +8 -0
- data/.idea/modules.xml +9 -0
- data/.idea/skewer.iml +61 -0
- data/.idea/vcs.xml +7 -0
- data/.rvmrc +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +146 -0
- data/LICENSE +13 -0
- data/README.md +77 -0
- data/Rakefile +59 -0
- data/Vagrantfile +5 -0
- data/assets/Gemfile +5 -0
- data/assets/rubygems.sh +39 -0
- data/bin/skewer +101 -0
- data/features/aws.feature +45 -0
- data/features/bootstrapper.feature +14 -0
- data/features/configuration.feature +15 -0
- data/features/delete.feature +22 -0
- data/features/hooks.feature +15 -0
- data/features/interface.feature +61 -0
- data/features/provision.feature +38 -0
- data/features/rackspace.feature +34 -0
- data/features/step_definitions/cucumber_steps.rb +43 -0
- data/features/step_definitions/delete.rb +32 -0
- data/features/support/env.rb +6 -0
- data/features/support/puppetcode/manifests/classes/foobar.pp +4 -0
- data/features/support/puppetcode/manifests/classes/foobroken.pp +4 -0
- data/features/support/puppetcode/manifests/classes/role.pp +3 -0
- data/features/support/puppetcode/manifests/site.pp +2 -0
- data/features/support/puppetcode/modules/foo/manifests/bar.pp +3 -0
- data/features/support/puppetcode/modules/foo/manifests/broken.pp +8 -0
- data/features/support/puppetcode/modules/puppet/manifests/munge.pp +5 -0
- data/features/update.feature +29 -0
- data/lib/aws/node.rb +52 -0
- data/lib/aws/security_group.rb +37 -0
- data/lib/aws/service.rb +21 -0
- data/lib/bootstrapper.rb +112 -0
- data/lib/cli.rb +96 -0
- data/lib/config.rb +67 -0
- data/lib/cuke.rb +26 -0
- data/lib/ersatz/ersatz_node.rb +31 -0
- data/lib/ersatz/ssh_result.rb +15 -0
- data/lib/eucalyptus.rb +15 -0
- data/lib/hooks.rb +22 -0
- data/lib/node.erb +5 -0
- data/lib/node.rb +43 -0
- data/lib/parser.rb +92 -0
- data/lib/puppet.rb +53 -0
- data/lib/puppet_node.rb +26 -0
- data/lib/puppet_runtime_error.rb +6 -0
- data/lib/rackspace/images.rb +25 -0
- data/lib/rackspace/node.rb +60 -0
- data/lib/skewer.rb +13 -0
- data/lib/skewer/version.rb +3 -0
- data/lib/source.rb +54 -0
- data/lib/stub_node.rb +25 -0
- data/lib/util.rb +17 -0
- data/skewer.gemspec +30 -0
- data/spec/aws/node_spec.rb +70 -0
- data/spec/aws/security_group_spec.rb +20 -0
- data/spec/aws/service_spec.rb +31 -0
- data/spec/bootstrapper_spec.rb +116 -0
- data/spec/cli_spec.rb +71 -0
- data/spec/config_spec.rb +68 -0
- data/spec/cuke_spec.rb +46 -0
- data/spec/ersatz_node_spec.rb +9 -0
- data/spec/ersatz_ssh_result_spec.rb +16 -0
- data/spec/hooks_spec.rb +19 -0
- data/spec/logger_spec.rb +22 -0
- data/spec/parser_spec.rb +93 -0
- data/spec/puppet.rb +47 -0
- data/spec/puppet_node_spec.rb +31 -0
- data/spec/rackspace/images_spec.rb +37 -0
- data/spec/rackspace/node_spec.rb +30 -0
- data/spec/source_spec.rb +45 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/features/example.feature +9 -0
- data/spec/support/features/step_definitions/example.rb +8 -0
- data/spec/util_spec.rb +27 -0
- 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
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
|
data/lib/puppet_node.rb
ADDED
@@ -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,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
|