remy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|