remy 0.0.1
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/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +235 -0
- data/Rakefile +10 -0
- data/chef/cookbooks/hello_world/recipes/default.rb +13 -0
- data/chef/cookbooks/hello_world/templates/default/hello_world.txt.erb +6 -0
- data/chef/roles/test_role.rb +3 -0
- data/chef/roles/test_role2.json +9 -0
- data/chef/spec/hello_world/default_spec.rb +6 -0
- data/lib/remy/bootstrap.rb +82 -0
- data/lib/remy/chef.rb +135 -0
- data/lib/remy/remy.rb +140 -0
- data/lib/remy/server.rb +71 -0
- data/lib/remy/shell.rb +26 -0
- data/lib/remy/version.rb +3 -0
- data/lib/remy.rb +56 -0
- data/lib/tasks/remy.rake +51 -0
- data/remy.gemspec +31 -0
- data/spec/fixtures/bar.yml +7 -0
- data/spec/fixtures/chef.yml +44 -0
- data/spec/fixtures/foo.yml +5 -0
- data/spec/fixtures/hello_world_chef.yml +8 -0
- data/spec/remy/bootstrap_spec.rb +61 -0
- data/spec/remy/chef_spec.rb +82 -0
- data/spec/remy/integration/chef_spec.rb +41 -0
- data/spec/remy_spec.rb +330 -0
- data/spec/spec_helper.rb +16 -0
- metadata +230 -0
data/lib/remy/remy.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Gregory S. Woodward
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
module Remy
|
25
|
+
class Configuration
|
26
|
+
attr_accessor :yml_files, :cookbook_path, :spec_path, :remote_chef_dir, :node_attributes, :roles_path
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@yml_files = []
|
30
|
+
@node_attributes = {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
include ::Remy::Shell
|
36
|
+
include FileUtils
|
37
|
+
|
38
|
+
def configure
|
39
|
+
@config_instance = Configuration.new
|
40
|
+
yield @config_instance
|
41
|
+
@configuration = Hashie::Mash.new({:yml_files => [@config_instance.yml_files].compact.flatten,
|
42
|
+
:remote_chef_dir => (@config_instance.remote_chef_dir || '/var/chef'),
|
43
|
+
:roles_path => [@config_instance.roles_path].compact.flatten,
|
44
|
+
:spec_path => [@config_instance.spec_path].compact.flatten,
|
45
|
+
:cookbook_path => [@config_instance.cookbook_path].compact.flatten}.merge!(@config_instance.node_attributes))
|
46
|
+
|
47
|
+
@config_instance.yml_files.each do |filename|
|
48
|
+
begin
|
49
|
+
configuration.deep_merge!(YAML.load(ERB.new(File.read(filename)).result) || {})
|
50
|
+
rescue SystemCallError, IOError
|
51
|
+
warn "WARN: #{filename} could not be found!"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def configuration
|
57
|
+
@configuration ? @configuration : Hashie::Mash.new
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_json
|
61
|
+
configuration.to_json
|
62
|
+
end
|
63
|
+
|
64
|
+
def servers
|
65
|
+
configuration.servers
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_servers(options = {})
|
69
|
+
return nil unless configuration.servers
|
70
|
+
Hashie::Mash.new(configuration.servers.inject({}) do |hash, (server_name, server_config)|
|
71
|
+
found = options.all? { |(key, value)| server_config[key] == value }
|
72
|
+
hash[server_name] = server_config if found
|
73
|
+
hash
|
74
|
+
end)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_server(options = {})
|
78
|
+
return nil unless configuration.servers
|
79
|
+
server_name, server_config = configuration.servers.detect do |(server_name, server_config)|
|
80
|
+
options.all? { |(key, value)| server_config[key] == value }
|
81
|
+
end
|
82
|
+
{server_name => server_config.nil? ? nil : server_config.dup}
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_server_config(options = {})
|
86
|
+
find_server(options).try(:values).try(:first)
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_server_config_by_name(name)
|
90
|
+
return nil unless configuration.servers
|
91
|
+
configuration.servers.find {|(server_name, _)| server_name == name}.try(:last)
|
92
|
+
end
|
93
|
+
|
94
|
+
def cloud_configuration
|
95
|
+
configuration && configuration.cloud_configuration
|
96
|
+
end
|
97
|
+
|
98
|
+
def bootstrap
|
99
|
+
configuration && configuration.bootstrap
|
100
|
+
end
|
101
|
+
|
102
|
+
def determine_ip_addresses_for_remy_run(rake_args)
|
103
|
+
ip_addresses = []
|
104
|
+
if options_hash = convert_properties_to_hash(rake_args)
|
105
|
+
servers = find_servers(options_hash)
|
106
|
+
if !servers.empty?
|
107
|
+
ip_addresses = servers.collect {|server_name, chef_option| chef_option.ip_address }
|
108
|
+
else
|
109
|
+
ip_addresses = [options_hash[:ip_address]]
|
110
|
+
end
|
111
|
+
else
|
112
|
+
names_or_ip_addresses = rake_args.split(' ').collect {|name| name.strip }
|
113
|
+
names_or_ip_addresses.each do |name_or_ip_address|
|
114
|
+
# From: http://www.regular-expressions.info/examples.html
|
115
|
+
ip_address_regex = '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b'
|
116
|
+
if name_or_ip_address.match(ip_address_regex)
|
117
|
+
ip_addresses << name_or_ip_address
|
118
|
+
elsif server_config = find_server_config_by_name(name_or_ip_address)
|
119
|
+
ip_addresses << server_config.ip_address
|
120
|
+
end
|
121
|
+
end
|
122
|
+
ip_addresses << configuration.ip_address
|
123
|
+
end
|
124
|
+
ip_addresses.compact
|
125
|
+
end
|
126
|
+
|
127
|
+
# Converts "foo:bar baz:blech" to {:foo => 'bar', :baz => 'blech'}
|
128
|
+
def convert_properties_to_hash(properties)
|
129
|
+
if properties =~ /:/
|
130
|
+
properties.split(' ').inject({}) do |result, pair|
|
131
|
+
key, value = pair.split(':')
|
132
|
+
result[key] = value
|
133
|
+
result
|
134
|
+
end.symbolize_keys
|
135
|
+
else
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/remy/server.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Remy::Server
|
2
|
+
attr_reader :server_name, :cloud_api_key, :cloud_username, :cloud_provider, :flavor_id, :image_id, :server
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
options = {
|
6
|
+
:flavor_id => 4, # 2GB
|
7
|
+
:image_id => 49, # Ubuntu 10.04 LTS (lucid)
|
8
|
+
:quiet => false
|
9
|
+
}.merge(Remy.cloud_configuration || {}).merge(options || {}).symbolize_keys
|
10
|
+
|
11
|
+
@server_name = options[:server_name]
|
12
|
+
@cloud_api_key = options[:cloud_api_key]
|
13
|
+
@cloud_username = options[:cloud_username]
|
14
|
+
@cloud_provider = options[:cloud_provider]
|
15
|
+
@flavor_id = options[:flavor_id]
|
16
|
+
@image_id = options[:image_id]
|
17
|
+
@quiet = options[:quiet]
|
18
|
+
@raise_exception = options[:raise_exception]
|
19
|
+
@server = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def create
|
23
|
+
compute = Fog::Compute.new(
|
24
|
+
:provider => cloud_provider,
|
25
|
+
:rackspace_api_key => cloud_api_key,
|
26
|
+
:rackspace_username => cloud_username
|
27
|
+
)
|
28
|
+
|
29
|
+
@server = compute.servers.create(:flavor_id => flavor_id.to_i, :image_id => image_id.to_i, :name => server_name)
|
30
|
+
server.wait_for do
|
31
|
+
print '.'
|
32
|
+
STDOUT.flush
|
33
|
+
ready?
|
34
|
+
end
|
35
|
+
print server_info
|
36
|
+
{:ip_address => server.public_ip_address, :password => server.password}
|
37
|
+
rescue Exception => e
|
38
|
+
puts 'Failed!'
|
39
|
+
p e
|
40
|
+
raise e if raise_exception?
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def server_info
|
46
|
+
<<-SERVER_INFO
|
47
|
+
|
48
|
+
|
49
|
+
Server name: #{server.name}
|
50
|
+
root password: #{server.password}
|
51
|
+
RAM: #{server.flavor.ram} MB
|
52
|
+
Image: #{server.image.name}
|
53
|
+
IP address: #{server.public_ip_address}
|
54
|
+
Private IP address: #{server.addresses['private'][0]}
|
55
|
+
|
56
|
+
SERVER_INFO
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def print *args
|
61
|
+
Kernel.print(*args) unless quiet?
|
62
|
+
end
|
63
|
+
|
64
|
+
def quiet?
|
65
|
+
@quiet
|
66
|
+
end
|
67
|
+
|
68
|
+
def raise_exception?
|
69
|
+
@raise_exception
|
70
|
+
end
|
71
|
+
end
|
data/lib/remy/shell.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Remy
|
2
|
+
module Shell
|
3
|
+
def execute(command)
|
4
|
+
if quiet?
|
5
|
+
`#{command} 2>&1`
|
6
|
+
$?.success?
|
7
|
+
else
|
8
|
+
puts "Running command: #{command}"
|
9
|
+
system command
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def remote_execute(cmd)
|
14
|
+
raise ArgumentError.new unless ip_address
|
15
|
+
execute "ssh -T #{user}@#{ip_address} '#{cmd.strip}'"
|
16
|
+
end
|
17
|
+
|
18
|
+
def user
|
19
|
+
'root'
|
20
|
+
end
|
21
|
+
|
22
|
+
def quiet?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/remy/version.rb
ADDED
data/lib/remy.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Gregory S. Woodward
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'active_support/core_ext/object/to_json'
|
26
|
+
rescue LoadError
|
27
|
+
require 'active_support/json/encoders/object'
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'active_support/core_ext/object/try'
|
32
|
+
rescue LoadError
|
33
|
+
require 'active_support/core_ext/try'
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'active_support/core_ext/hash'
|
37
|
+
require 'fog'
|
38
|
+
require 'erb'
|
39
|
+
require 'json'
|
40
|
+
require 'hashie'
|
41
|
+
require 'tmpdir'
|
42
|
+
require 'remy/shell'
|
43
|
+
require 'yaml'
|
44
|
+
dir = File.dirname(__FILE__)
|
45
|
+
Dir[File.join(File.dirname(__FILE__), 'remy', '**', '*.rb')].each {|f| require f.gsub(dir, '')[1, f.length] }
|
46
|
+
|
47
|
+
module Remy
|
48
|
+
begin
|
49
|
+
class Railtie < ::Rails::Railtie
|
50
|
+
rake_tasks do
|
51
|
+
load 'tasks/remy.rake'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue LoadError, NameError
|
55
|
+
end
|
56
|
+
end
|
data/lib/tasks/remy.rake
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'remy'
|
2
|
+
|
3
|
+
namespace :remy do
|
4
|
+
task :environment do
|
5
|
+
begin
|
6
|
+
Rake::Task[:environment].invoke
|
7
|
+
rescue RuntimeError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'ssh to a named box'
|
12
|
+
task :ssh, :rake_args do |task, options|
|
13
|
+
Rake::Task[:'remy:environment'].invoke
|
14
|
+
user = Remy.configuration.user || 'root'
|
15
|
+
if ip_address = Remy.determine_ip_addresses_for_remy_run(options[:rake_args]).try(:first)
|
16
|
+
exec "ssh #{user}@#{ip_address}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :chef do
|
21
|
+
desc 'run chef solo'
|
22
|
+
task :run, :rake_args do |task, options|
|
23
|
+
Rake::Task[:'remy:environment'].invoke
|
24
|
+
Remy::Chef.rake_run(options[:rake_args])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :server do
|
29
|
+
desc 'create a server'
|
30
|
+
task :create, :server_name, :flavor_id, :cloud_api_key, :cloud_username, :cloud_provider, :image_id do |task, options|
|
31
|
+
Rake::Task[:'remy:environment'].invoke
|
32
|
+
Remy::Server.new(options).create
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'bootstrap chef'
|
36
|
+
task :bootstrap, :ip_address, :password do |task, options|
|
37
|
+
Rake::Task[:'remy:environment'].invoke
|
38
|
+
Remy::Bootstrap.new(options).run
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'create a server and bootstrap chef'
|
42
|
+
task :create_and_bootstrap, :server_name, :flavor_id, :cloud_api_key, :cloud_username, :cloud_provider, :image_id do |task, options|
|
43
|
+
Rake::Task[:'remy:environment'].invoke
|
44
|
+
begin
|
45
|
+
result = Remy::Server.new({:raise_exception => true}.merge(options)).create
|
46
|
+
Rake::Task[:'remy:server:bootstrap'].invoke(result[:ip_address], result[:password])
|
47
|
+
rescue Exception => e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/remy.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'remy/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'remy'
|
7
|
+
s.version = Remy::VERSION
|
8
|
+
s.authors = ['Greg Woodward & Ryan Dy']
|
9
|
+
s.email = ['pair+gwoodward+rdy@pivotallabs.com']
|
10
|
+
s.homepage = 'http://sharespost.com'
|
11
|
+
s.summary = %q{Remy gem}
|
12
|
+
s.description = %q{Easy chef deployment}
|
13
|
+
|
14
|
+
s.rubyforge_project = 'remy'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency 'bourne'
|
23
|
+
s.add_development_dependency 'mocha'
|
24
|
+
s.add_development_dependency 'i18n'
|
25
|
+
s.add_development_dependency 'json'
|
26
|
+
s.add_development_dependency 'rspec', '~> 2.7.0'
|
27
|
+
s.add_runtime_dependency 'activesupport', '>= 2'
|
28
|
+
s.add_runtime_dependency 'chef'
|
29
|
+
s.add_runtime_dependency 'fog'
|
30
|
+
s.add_runtime_dependency 'hashie'
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
app_server: &APP
|
2
|
+
recipes:
|
3
|
+
- recipe[hello_world]
|
4
|
+
database_server: &DB
|
5
|
+
adapter: mysql2
|
6
|
+
encoding: utf8
|
7
|
+
|
8
|
+
rails_env: bogus_and_should_be_overridden_by_value_in_servers_section
|
9
|
+
|
10
|
+
servers:
|
11
|
+
web.sharespost.com:
|
12
|
+
<<: *APP
|
13
|
+
ip_address: <%= IP_ADDRESS %>
|
14
|
+
rails_env: demo
|
15
|
+
color: blue
|
16
|
+
|
17
|
+
db.sharespost.com:
|
18
|
+
<<: *DB
|
19
|
+
ip_address: 51.51.51.51
|
20
|
+
rails_env: production
|
21
|
+
color: yellow
|
22
|
+
|
23
|
+
demo.sharespost.com:
|
24
|
+
<<: *DB
|
25
|
+
<<: *APP
|
26
|
+
ip_address: 52.52.52.52
|
27
|
+
rails_env: demo
|
28
|
+
color: green
|
29
|
+
|
30
|
+
cloud_configuration:
|
31
|
+
cloud_provider: Rackspace
|
32
|
+
cloud_username: sharespost
|
33
|
+
cloud_api_key: abcdefg12345
|
34
|
+
flavor_id: 4
|
35
|
+
image_id: 49
|
36
|
+
server_name: new-server.somedomain.com
|
37
|
+
|
38
|
+
bootstrap:
|
39
|
+
ruby_version: 1.9.2
|
40
|
+
gems:
|
41
|
+
bundler: 1.0.21
|
42
|
+
chef: 0.10.4
|
43
|
+
rspec: 2.7.0
|
44
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Remy::Bootstrap do
|
4
|
+
def ruby_version(bootstrap)
|
5
|
+
bootstrap.instance_variable_get(:@ruby_version)
|
6
|
+
end
|
7
|
+
|
8
|
+
def gem(bootstrap)
|
9
|
+
bootstrap.instance_variable_get(:@gems)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ip_address(bootstrap)
|
13
|
+
bootstrap.instance_variable_get(:@ip_address)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "ruby_version" do
|
17
|
+
it 'should default to 1.8.7 if there is no Ruby version specified in the yml files' do
|
18
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/hello_world_chef.yml') }
|
19
|
+
bootstrap = Remy::Bootstrap.new
|
20
|
+
ruby_version(bootstrap).should == '1.8.7'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should get the Ruby version if specified in the yml files' do
|
24
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
25
|
+
bootstrap = Remy::Bootstrap.new
|
26
|
+
ruby_version(bootstrap).should == '1.9.2'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should use the version passed in as an option, even if it exists in the yml files' do
|
30
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
31
|
+
bootstrap = Remy::Bootstrap.new(:ruby_version => '1.9.1')
|
32
|
+
ruby_version(bootstrap).should == '1.9.1'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "gems" do
|
37
|
+
it 'should default to nil if not specified in the yml files' do
|
38
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/hello_world_chef.yml') }
|
39
|
+
bootstrap = Remy::Bootstrap.new
|
40
|
+
gem(bootstrap)[:chef].should be_nil
|
41
|
+
gem(bootstrap)[:bundler].should be_nil
|
42
|
+
gem(bootstrap)[:rspec].should be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should get gem version if it has been specified in the yml files' do
|
46
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
47
|
+
bootstrap = Remy::Bootstrap.new
|
48
|
+
gem(bootstrap)[:chef].should == '0.10.4'
|
49
|
+
gem(bootstrap)[:bundler].should == '1.0.21'
|
50
|
+
gem(bootstrap)[:rspec].should == '2.7.0'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "ip_address" do
|
55
|
+
it 'should use the value from the options' do
|
56
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/hello_world_chef.yml') }
|
57
|
+
bootstrap = Remy::Bootstrap.new(:ip_address => '1.2.3.4', :password => 'abcdef')
|
58
|
+
ip_address(bootstrap).should == '1.2.3.4'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Remy::Chef do
|
4
|
+
def node_configuration(chef)
|
5
|
+
chef.instance_variable_get(:@node_configuration)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#initialize and the node configuration" do
|
9
|
+
it 'should use the top-level IP address in the yml files, if one is present, and an ip address is not passed in as an argument' do
|
10
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/hello_world_chef.yml') }
|
11
|
+
chef = Remy::Chef.new
|
12
|
+
node_configuration(chef).ip_address.should == IP_ADDRESS
|
13
|
+
node_configuration(chef).color.should == 'blue'
|
14
|
+
node_configuration(chef).recipes.should == ['recipe[hello_world]']
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should allow the top-level values in the yml files (including the ip address) to be overridden' do
|
18
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/hello_world_chef.yml') }
|
19
|
+
chef = Remy::Chef.new(:ip_address => '1.2.3.4', :color => 'green')
|
20
|
+
node_configuration(chef).ip_address.should == '1.2.3.4'
|
21
|
+
node_configuration(chef).color.should == 'green'
|
22
|
+
node_configuration(chef).recipes.should == ['recipe[hello_world]']
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return properties from the :servers section of the yml file if the ip address is found in there' do
|
26
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
27
|
+
chef = Remy::Chef.new(:ip_address => '51.51.51.51')
|
28
|
+
node_configuration(chef).ip_address.should == '51.51.51.51'
|
29
|
+
node_configuration(chef).rails_env.should == 'production'
|
30
|
+
node_configuration(chef).color.should == 'yellow'
|
31
|
+
node_configuration(chef).adapter.should == 'mysql2'
|
32
|
+
node_configuration(chef).encoding.should == 'utf8'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should allow properties from the servers section of the yml file to be overridden plus additional options added' do
|
36
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
37
|
+
chef = Remy::Chef.new(:ip_address => '51.51.51.51', :color => 'purple', :temperature => 'warm')
|
38
|
+
node_configuration(chef).color.should == 'purple' # Overrides 'yellow' from the yml files
|
39
|
+
node_configuration(chef).temperature.should == 'warm' # A new node attribute which is not present in the yml files
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should allow the chef args to be specified (and not merge this chef_args value into the node configuration)' do
|
43
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
44
|
+
chef = Remy::Chef.new(:chef_args => '-l debug')
|
45
|
+
node_configuration(chef).chef_args.should be_nil
|
46
|
+
chef.instance_variable_get(:@chef_args).should == '-l debug'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should allow the quiet option to be specified (and not merge this option into the node configuration)' do
|
50
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
51
|
+
chef = Remy::Chef.new(:quiet => true)
|
52
|
+
node_configuration(chef).quiet.should be_nil
|
53
|
+
chef.instance_variable_get(:@quiet).should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should not modify the global Remy configuration, but rather only the node configuration for this particular Chef node' do
|
57
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/bar.yml') }
|
58
|
+
Remy.configuration.another_node_attribute.should == 'hot'
|
59
|
+
chef = Remy::Chef.new(:another_node_attribute => 'cold')
|
60
|
+
node_configuration(chef).another_node_attribute.should == 'cold'
|
61
|
+
Remy.configuration.another_node_attribute.should == 'hot' # Unchanged from its original value # do some checks
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#run' do
|
66
|
+
before do
|
67
|
+
Remy.configure { |config| config.yml_files = File.join(File.dirname(__FILE__), '../fixtures/chef.yml') }
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should work with a hash as its argument' do
|
71
|
+
chef = Remy::Chef.new(:ip_address => IP_ADDRESS)
|
72
|
+
node_configuration(chef).ip_address.should == IP_ADDRESS
|
73
|
+
node_configuration(chef).recipes.should == ['recipe[hello_world]']
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should work with JSON as its argument' do
|
77
|
+
chef = Remy::Chef.new("{\"ip_address\":\"#{IP_ADDRESS}\"}")
|
78
|
+
node_configuration(chef).ip_address.should == IP_ADDRESS
|
79
|
+
node_configuration(chef).recipes.should == ['recipe[hello_world]']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Remy::Chef do
|
4
|
+
before do
|
5
|
+
Remy.configure do |config|
|
6
|
+
config.yml_files = ['../../fixtures/foo.yml', '../../fixtures/bar.yml', '../../fixtures/chef.yml'].map { |f| File.join(File.dirname(__FILE__), f) }
|
7
|
+
config.cookbook_path = ["../../../chef/cookbooks"].map { |f| File.join(File.dirname(__FILE__), f) }
|
8
|
+
config.spec_path = ["../../../chef/spec"].map { |f| File.join(File.dirname(__FILE__), f) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#run' do
|
13
|
+
def clean_up_remote_chef_test_files(chef)
|
14
|
+
chef.remote_execute "rm -rf /tmp/hello_world.txt #{Remy.configuration.remote_chef_dir}" if Remy.configuration.remote_chef_dir && Remy.configuration.remote_chef_dir.size > 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def verify_contents_of_hello_world(chef)
|
18
|
+
chef.remote_execute(%Q{cat /tmp/hello_world.txt | grep "I am feeling blue"}).should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_chef(options = {})
|
22
|
+
chef = Remy::Chef.new(options)
|
23
|
+
clean_up_remote_chef_test_files(chef)
|
24
|
+
chef.run
|
25
|
+
yield chef
|
26
|
+
clean_up_remote_chef_test_files(chef)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should work with a hash as its argument' do
|
30
|
+
run_chef(:ip_address => IP_ADDRESS) do |chef|
|
31
|
+
verify_contents_of_hello_world(chef)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should work with JSON as its argument' do
|
36
|
+
run_chef("{\"ip_address\":\"#{IP_ADDRESS}\"}") do |chef|
|
37
|
+
verify_contents_of_hello_world(chef)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|