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
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: updating a node
|
2
|
+
In order to run my puppet code again
|
3
|
+
As a someone who wants to update config on the machine
|
4
|
+
I want to run the update command
|
5
|
+
|
6
|
+
Scenario: run the command without args
|
7
|
+
When I run `./bin/skewer update`
|
8
|
+
Then the exit status should not be 0
|
9
|
+
|
10
|
+
@announce-stdout @announce-stderr
|
11
|
+
Scenario: pass in a hostname user and role on passing puppet code
|
12
|
+
Given I have access to the internet
|
13
|
+
And I have puppet code in "/tmp/skewer_test_code"
|
14
|
+
When I run `./bin/skewer update --host default --user vagrant --role foobar --puppetcode /tmp/skewer_test_code/`
|
15
|
+
Then the exit status should be 0
|
16
|
+
And the stdout should contain "Using Puppet Code from /tmp/skewer_test_code/"
|
17
|
+
And the stdout should contain "Puppet run succeeded"
|
18
|
+
|
19
|
+
|
20
|
+
@announce-stdout @announce-stderr
|
21
|
+
Scenario: pass in a hostname user and role on broken puppet code
|
22
|
+
Given I have access to the internet
|
23
|
+
And I have puppet code in "/tmp/skewer_test_code"
|
24
|
+
When I run `./bin/skewer update --host default --user vagrant --role foobroken --puppetcode /tmp/skewer_test_code/`
|
25
|
+
Then the exit status should be 0
|
26
|
+
And the stdout should contain "Using Puppet Code from /tmp/skewer_test_code/"
|
27
|
+
And the stdout should contain "Puppet failed"
|
28
|
+
|
29
|
+
|
data/lib/aws/node.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'skewer'
|
2
|
+
require 'aws/service'
|
3
|
+
|
4
|
+
module Skewer
|
5
|
+
module AWS
|
6
|
+
# Build out an AWS node using Fog.
|
7
|
+
class Node
|
8
|
+
attr_reader :node
|
9
|
+
|
10
|
+
def initialize(aws_id, group_names, options = {})
|
11
|
+
if options[:aws_node]
|
12
|
+
@node = options[:aws_node]
|
13
|
+
else
|
14
|
+
@service = self.class.find_service(options)
|
15
|
+
node_options = {
|
16
|
+
:image_id => aws_id,
|
17
|
+
:flavor_id => SkewerConfig.get('flavor_id'),
|
18
|
+
:username => SkewerConfig.get('aws_username'),
|
19
|
+
:groups => group_names
|
20
|
+
}
|
21
|
+
|
22
|
+
if options[:key_name]
|
23
|
+
node_options[:key_name] = options[:key_name]
|
24
|
+
end
|
25
|
+
|
26
|
+
if SkewerConfig.instance.get('key_name')
|
27
|
+
node_options[:key_name] = SkewerConfig.get('key_name')
|
28
|
+
end
|
29
|
+
|
30
|
+
@node = @service.servers.bootstrap(node_options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.find_service(options)
|
35
|
+
options[:service] ? options[:service] : AWS::Service.new.service
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy
|
39
|
+
@node.destroy
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.find_by_name(dns_name, service = self.find_service({}))
|
43
|
+
node = service.servers.select { |server| server.dns_name == dns_name }[0]
|
44
|
+
if node
|
45
|
+
return self.new(nil, nil, {:aws_node => node})
|
46
|
+
else
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Skewer
|
2
|
+
module AWS
|
3
|
+
# Security group permissions for our AWS service.
|
4
|
+
class SecurityGroup
|
5
|
+
attr_reader :service, :group
|
6
|
+
|
7
|
+
def initialize(name, desc, ports)
|
8
|
+
@service ||= SkewerConfig.get 'aws_service'
|
9
|
+
groups = @service.security_groups
|
10
|
+
group = groups.select { |group| group.name == name }[0]
|
11
|
+
|
12
|
+
if group.nil? == true
|
13
|
+
group = @service.create_security_group(name, desc)
|
14
|
+
group = groups.get(name)
|
15
|
+
end
|
16
|
+
@group = group
|
17
|
+
|
18
|
+
if ports.length >= 1
|
19
|
+
ensure_port_ranges(group, ports)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def ensure_port_ranges(group, ports)
|
25
|
+
ports.each do |port|
|
26
|
+
|
27
|
+
description = port[:description]
|
28
|
+
range = port[:range]
|
29
|
+
|
30
|
+
group.revoke_port_range(range)
|
31
|
+
group.authorize_port_range(range, {:name => description})
|
32
|
+
# TODO: get the port range options in there
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/aws/service.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Skewer
|
2
|
+
module AWS
|
3
|
+
# The AWS service which is used to interface through to the AWS
|
4
|
+
# cloud using Fog.
|
5
|
+
class Service
|
6
|
+
attr_reader :service
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
region = SkewerConfig.get('region')
|
10
|
+
@service = Fog::Compute.new({
|
11
|
+
:provider => 'AWS',
|
12
|
+
:region => region})
|
13
|
+
SkewerConfig.set 'aws_service', @service
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.service
|
17
|
+
self.new.service
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/bootstrapper.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'config'
|
2
|
+
require 'util'
|
3
|
+
require 'skewer'
|
4
|
+
|
5
|
+
module Skewer
|
6
|
+
# puts all of puppet's dependencies on
|
7
|
+
class Bootstrapper
|
8
|
+
MAX_CACHE = 3600
|
9
|
+
attr_writer :mock
|
10
|
+
|
11
|
+
def initialize(node,options)
|
12
|
+
@node = node
|
13
|
+
@options = options
|
14
|
+
@util = Util.new
|
15
|
+
@mock = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_ssh_hostkey
|
19
|
+
location = @util.get_location(@node)
|
20
|
+
system "ssh -o 'StrictHostKeyChecking no' -o 'PasswordAuthentication no' no_such_user@#{location} >/dev/null 2>&1"
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute(file_name)
|
24
|
+
file = File.join(File.dirname(__FILE__), '..', 'assets', file_name)
|
25
|
+
raise "#{file} does not exist" unless File.exists? file
|
26
|
+
@node.scp file, '/var/tmp/.'
|
27
|
+
result = @node.ssh "sudo bash /var/tmp/#{file_name}"
|
28
|
+
puts result.inspect
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def install_gems
|
33
|
+
Skewer.logger.debug "Installing Gems"
|
34
|
+
assets = File.join(File.dirname(__FILE__), '..', 'assets')
|
35
|
+
@node.scp File.join(File.expand_path(assets), 'Gemfile'), 'infrastructure'
|
36
|
+
command = ". /etc/profile.d/rubygems.sh && cd infrastructure && bundle install"
|
37
|
+
result = @node.ssh(command)
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_key_to_agent(executor = Kernel, homedir = ENV['HOME'])
|
41
|
+
config = SkewerConfig.instance
|
42
|
+
key_name = config.get('key_name')
|
43
|
+
key_path = File.join(homedir, '.ssh', "#{key_name}.pem")
|
44
|
+
Skewer.logger.debug "****Looking for #{key_path}"
|
45
|
+
if File.exists?(key_path)
|
46
|
+
executor.system("ssh-add #{key_path}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def sync_source()
|
51
|
+
require 'source'
|
52
|
+
require 'puppet_node'
|
53
|
+
config = SkewerConfig.instance
|
54
|
+
source_dir = config.get(:puppet_repo)
|
55
|
+
Skewer.logger.debug "Using Puppet Code from #{source_dir}"
|
56
|
+
role = @options[:role]
|
57
|
+
if role
|
58
|
+
PuppetNode.new({:default => role.to_sym}).render
|
59
|
+
end
|
60
|
+
# TODO: if there's no role, it should look it up from an external source
|
61
|
+
if @mock
|
62
|
+
Skewer.logger.debug "Mock: would normally rsync now"
|
63
|
+
else
|
64
|
+
Source.new(source_dir).rsync(@node)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def lock_file
|
70
|
+
File.join('/tmp', 'skewer-' + Util.new.get_location(@node))
|
71
|
+
end
|
72
|
+
|
73
|
+
def lock_file_expired?(lock_file)
|
74
|
+
now = Time.now
|
75
|
+
lock_file_time = File.stat(lock_file).mtime
|
76
|
+
age = now - lock_file_time
|
77
|
+
age > MAX_CACHE
|
78
|
+
end
|
79
|
+
|
80
|
+
def destroy_lock_file()
|
81
|
+
FileUtils.rm_f(self.lock_file)
|
82
|
+
end
|
83
|
+
|
84
|
+
def should_i_run?
|
85
|
+
lock_file = lock_file()
|
86
|
+
if File.exists?(lock_file)
|
87
|
+
if lock_file_expired?(lock_file)
|
88
|
+
destroy_lock_file
|
89
|
+
return true
|
90
|
+
else
|
91
|
+
return false
|
92
|
+
end
|
93
|
+
else
|
94
|
+
FileUtils.touch(lock_file)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def prepare_node
|
100
|
+
add_ssh_hostkey
|
101
|
+
execute('rubygems.sh')
|
102
|
+
add_key_to_agent
|
103
|
+
end
|
104
|
+
|
105
|
+
def go
|
106
|
+
i_should_run = should_i_run?
|
107
|
+
prepare_node() if i_should_run
|
108
|
+
sync_source
|
109
|
+
install_gems if i_should_run
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fog'
|
3
|
+
|
4
|
+
require 'skewer'
|
5
|
+
require 'bootstrapper'
|
6
|
+
require 'util'
|
7
|
+
require 'hooks'
|
8
|
+
|
9
|
+
module Skewer
|
10
|
+
# this is responsible for composing all the other components. or should be.
|
11
|
+
class CLI
|
12
|
+
attr_reader :bootstrapper, :node
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
@options = options
|
16
|
+
@config = SkewerConfig.instance
|
17
|
+
@config.slurp_options(options)
|
18
|
+
@util = Util.new
|
19
|
+
@config.set(:logger, Skewer.logger)
|
20
|
+
end
|
21
|
+
|
22
|
+
def select_node(kind)
|
23
|
+
Skewer.logger.debug "Evaluating cloud #{kind}"
|
24
|
+
image = @options[:image]
|
25
|
+
case kind
|
26
|
+
when :ec2
|
27
|
+
require 'aws/security_group'
|
28
|
+
require 'aws/node'
|
29
|
+
require 'aws/service'
|
30
|
+
Skewer.logger.debug 'Launching an EC2 node'
|
31
|
+
aws_group = @options[:group]
|
32
|
+
group = aws_group ? aws_group : 'default'
|
33
|
+
node = AWS::Node.new(image, [group]).node
|
34
|
+
when :rackspace
|
35
|
+
require 'rackspace/node'
|
36
|
+
Skewer.logger.debug 'Launching a Rackspace node'
|
37
|
+
node = Rackspace::Node.new(@options[:flavor_id], image, 'default').node
|
38
|
+
when :linode
|
39
|
+
raise "not implemented"
|
40
|
+
when :eucalyptus
|
41
|
+
Skewer.logger.debug 'Using the EC2 API'
|
42
|
+
require 'eucalyptus'
|
43
|
+
node = Eucalyptus.new
|
44
|
+
when :vagrant
|
45
|
+
Skewer.logger.debug 'Launching a local vagrant node'
|
46
|
+
require 'ersatz/ersatz_node.rb'
|
47
|
+
node = ErsatzNode.new('default', 'vagrant')
|
48
|
+
when :stub
|
49
|
+
Skewer.logger.debug "Launching stubbed node for testing"
|
50
|
+
require 'stub_node'
|
51
|
+
node = StubNode.new
|
52
|
+
when :ersatz
|
53
|
+
require 'ersatz/ersatz_node.rb'
|
54
|
+
node = ErsatzNode.new(@config.get('host'), @config.get('user'))
|
55
|
+
else
|
56
|
+
raise "I don't know that cloud"
|
57
|
+
end
|
58
|
+
node
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy
|
62
|
+
@node.destroy unless @options[:keep]
|
63
|
+
end
|
64
|
+
|
65
|
+
def bootstrap
|
66
|
+
node = select_node(@options[:kind])
|
67
|
+
@node = node
|
68
|
+
@bootstrapper = Bootstrapper.new(node, @options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def go
|
72
|
+
require 'puppet'
|
73
|
+
require 'cuke'
|
74
|
+
begin
|
75
|
+
node = @node
|
76
|
+
node.wait_for { ready? }
|
77
|
+
@bootstrapper.go
|
78
|
+
Puppet.run(node, @options)
|
79
|
+
location = @util.get_location(node)
|
80
|
+
Hooks.new(location).run
|
81
|
+
Skewer.logger.debug "Node ready\n open http://#{location} or \n ssh -l #{node.username} #{location}"
|
82
|
+
Cuke.new(@config.get(:cuke_dir)).run if @config.get(:cuke_dir)
|
83
|
+
rescue Exception => exception
|
84
|
+
Skewer.logger.debug exception
|
85
|
+
ensure
|
86
|
+
destroy
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.bootstrap_and_go(options)
|
91
|
+
skewer = self.new(options)
|
92
|
+
skewer.bootstrap
|
93
|
+
skewer.go
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/config.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Skewer
|
4
|
+
# responsible for all configuration, once I move all the options in
|
5
|
+
class SkewerConfig
|
6
|
+
include Singleton
|
7
|
+
attr_accessor :aws_service, :puppet_repo, :region, :flavor_id, :aws_username, :flavor_id
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
reset
|
11
|
+
read_config_files
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
15
|
+
@puppet_repo = '../infrastructure'
|
16
|
+
@region = 'us-east-1'
|
17
|
+
@flavor_id = 'm1.large'
|
18
|
+
@aws_username = 'ubuntu'
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_config_file(config_file)
|
22
|
+
if File.exists?(config_file)
|
23
|
+
Skewer.logger.debug "reading #{config_file}"
|
24
|
+
config = File.read(config_file)
|
25
|
+
parse(config)
|
26
|
+
Skewer.logger.debug self.inspect
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_config_files
|
31
|
+
read_config_file(File.join(ENV['HOME'], '.skewer.json'))
|
32
|
+
read_config_file('.skewer.json')
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse(config)
|
36
|
+
require 'json'
|
37
|
+
configz = JSON.parse(config)
|
38
|
+
configz.each { |key,value| set(key,value) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def set(attribute, value)
|
42
|
+
self.instance_variable_set "@#{attribute}", value
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.set(key,value)
|
46
|
+
instance = self.instance
|
47
|
+
instance.set(key,value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def get(attribute)
|
51
|
+
if attribute.class == Symbol
|
52
|
+
attribute = attribute.to_s
|
53
|
+
end
|
54
|
+
self.instance_variable_get "@" + attribute
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.get(key)
|
58
|
+
self.instance.get(key)
|
59
|
+
end
|
60
|
+
|
61
|
+
def slurp_options(options)
|
62
|
+
options.each_pair do |key, value|
|
63
|
+
self.set(key, value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|