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 ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .idea/*
6
+ junk*.*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.8.7-p352@remy --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in remy.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Gregory S. Woodward
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,235 @@
1
+ # [Remy](http://www.github.com/gregwoodward/remy)
2
+
3
+ Remy permits you to easily run chef-solo (one of the tools that is part of [Chef](http://www.opscode.com/chef/) from
4
+ [Opscode](http://www.opscode.com/)), whether from within a stand-alone Ruby application, or from within a Rails project.
5
+ Remy is "a little chef"; our contention is that you get 95-98% of the benefits of Chef without 95-98% of the hassles
6
+ by **NOT** using [chef-client](http://wiki.opscode.com/display/chef/Chef+Client) &
7
+ [chef-server](http://wiki.opscode.com/display/chef/Chef+Server), but rather just using
8
+ [chef-solo](http://wiki.opscode.com/display/chef/Chef+Solo); Remy provides the missing tools which are required to
9
+ easily use chef-solo. Remy allows you to build a remote cloud
10
+ server box easily (whether from Rackspace or from some other cloud service provider), and then bootstrap it so that it
11
+ can run chef-solo (the bootstrap will also run on physical, not cloud, boxes; all that's required is remote host at a valid IP address
12
+ for Remy to use). Remy then provides an easy and convenient way to run chef-solo against these remote boxes, either
13
+ programmatically or from some rake tasks that are provided. The ability to support a cluster of boxes plus multiple config files
14
+ & directories (where some of these config files might contain passwords on encrypted drives) is what distinguishes Remy
15
+ from [soloist](https://github.com/mkocher/soloist) and other chef-solo utilities; these multiple config files and
16
+ directories are then "blended" together to create an overall chef configuration (as JSON) for a particular chef node.
17
+
18
+ We also do Test-Driven Chef (TDC) in our example recipes; rspec specs are run on the remote boxes to verify that the
19
+ packages and configuration are installed properly; we use these specs to drive the creation of the Chef recipes.
20
+
21
+ Note that the Chef bootstrap portion of Remy is currently limited to Ubuntu (it contains references to 'apt-get'), but the
22
+ chef-solo part of Remy can run on any type of Unix if the box has been properly bootstrapped for Chef. Also note that it
23
+ would be relatively simple to extend Remy to support yum and CentOS/RedHat and other Unix OS's.
24
+
25
+
26
+ ## Basic usage:
27
+
28
+ See [the Remy simple Rails example](http://www.github.com/gregwoodward/remy_simple_rails_example) for the absolute simplest
29
+ Remy installation with a "Hello world" recipe; you can be up and running with chef-solo in under 5 minutes.
30
+ [The Remy cluster rails example](http://www.github.com/gregwoodward/remy_cluster_rails_example) shows a full Rails
31
+ installation with a clustered environment for staging and production, and a single box which hosts the entire Rails app
32
+ for the demo environment; multiple config files are used because passwords are stored separately on an encrypted drive.
33
+ These are the actual recipes we use for our clustered server environment at [SharesPost](http://www.sharespost.com/).
34
+
35
+
36
+ ## Concepts
37
+
38
+ Remy is configured as shown in an example Remy configuration file [config/initializers/remy.rb](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/config/initializers/remy.rb),
39
+ along with the example yml files in [chef/config/chef.yml](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml) and
40
+ [SecureEncryptedDrive/chef/config/chef.yml](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/SecureEncryptedDrive/chef/config/chef.yml)
41
+ (this 'SecureEncryptedDrive' would normally not be a part of the Rails project, but rather an encrypted disk volume which is mounted such as
42
+ /Volumes/SecureEncryptedDrive). To use Remy, create a cookbooks directory which contains all of your recipes (see
43
+ [chef/cookbooks](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/cookbooks) as an example;
44
+ these cookbook directories should be in the [standard Opscode Chef cookbooks format](http://wiki.opscode.com/display/chef/Cookbooks) ).
45
+ The location of the chef cookbooks directory is specified in the Remy configuration (see
46
+ [remy.rb](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/config/initializers/remy.rb)); the default
47
+ location is /var/chef on the remote boxes, and is typically put in RAILS_ROOT/chef/cookbooks in your local Rails app.
48
+ Within one of these YAML files, create an array of servers as specified by :servers (see
49
+ [chef/config/chef.yml](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml) ). The values in the yml config files
50
+ which are loaded last take precedence over those which were loaded earlier, and options passed directly into Remy take
51
+ precedence over values in the yml config files.
52
+
53
+ Remy is typically used programmatically in a Capistrano deploy file or other Rails code
54
+ (see [deploy.rb](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/deploy.rb)), or by using the supplied Remy rake tasks
55
+ (see [remy.rake](http://www.github.com/gregwoodward/remy/blob/master/lib/tasks/remy.rake)).
56
+
57
+ ### Remy yml config files
58
+
59
+ The Remy yml files can be pretty much in whatever format you desire with a few constraints; see
60
+ [this example for simple usage](https://github.com/gregwoodward/remy_simple_rails_example/blob/master/chef/config/chef.yml), and this one for
61
+ [an advanced example to support a cluster](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml).
62
+
63
+ These are keys which have special significance in the Remy config files; see :ip_address, :servers, and :cloud_configuration
64
+ [here](https://github.com/gregwoodward/remy_simple_rails_example/blob/master/chef/config/chef.yml),
65
+ [here](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml), and
66
+ [here](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/SecureEncryptedDrive/chef/config/chef.yml) for examples.
67
+
68
+ #### :ip_address
69
+
70
+ :ip_address is the IP address of the box which chef-solo will run against. Normally, this is given to Remy as part of the options
71
+ arguments (either programmatically, or from a rake task) and is not in the yml config files, but it could also come in from one of the Remy yml config file(s).
72
+ The simplest usage of Remy to run chef-solo is:
73
+
74
+ `Remy::Chef.new(:ip_address => '123.123.123.123').run`
75
+
76
+ where '123.123.123.123' is the IP address that chef-solo will run on. Note that:
77
+
78
+ `Remy::Chef.new.run`
79
+
80
+ would also work if :ip_address was specified somewhere in the Remy configuration (i.e., it was specified in one of the Remy yml files); this is
81
+ the case in the [chef.yml in the "hello world" Remy example](http://www.github.com/gregwoodward/remy_simple_rails_example/blob/master/chef/config/chef.yml)
82
+
83
+
84
+ #### :servers
85
+
86
+ If you are configuring a cluster of boxes, look at the [chef/config/chef.yml](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml)
87
+ file as an example. You'll see a :servers section; each server should specify its :ip_address. When Remy runs, Remy will
88
+ find the section of the :servers in the yml file which has the same :ip_address as the currently specified value, and
89
+ those values will get "promoted" to the top level, and applied against this chef node (i.e., JSON will be generated which
90
+ contains these values).
91
+
92
+ #### :cloud_configuration
93
+
94
+ You can specify the cloud server configuration in a :cloud_configuration section in one of the Remy config yml files;
95
+ see [SecureEncryptedDrive/chef/config/chef.yml](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/SecureEncryptedDrive/chef/config/chef.yml) for an example.
96
+ Alternatively, these cloud values can also be passed into the various Remy rake commands; see :server_name, :cloud_api_key, :cloud_username,
97
+ etc., in the Rake commands in [remy.rake](http://www.github.com/gregwoodward/remy/blob/master/lib/tasks/remy.rake).
98
+
99
+ #### :bootstrap
100
+
101
+ You can specify the Ruby version that gets installed on a new remote box, along with the versions of the various bootstrap
102
+ Ruby gems (chef, bundler, and rspec) in the yml config files; see
103
+ [chef.yml](http://www.github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/config/chef.yml) for an example
104
+ of these parameters.
105
+
106
+
107
+ ## Remy usage from Rake
108
+
109
+ ### Create a new cloud server
110
+
111
+ Use the following rake command to create a new cloud server:
112
+
113
+ `rake remy:server:create[:server_name, :flavor_id, :cloud_api_key, :cloud_username, :cloud_provider, :image_id]`
114
+
115
+ e.g.,
116
+
117
+ `rake remy:server:create[foobar.sharespost.com,4,abcdefg1234,sharespost,Rakespace,49]`
118
+
119
+ Note that if you have the :cloud_api_key, etc., specified in your Remy configuration yml files, you can omit those arguments to the
120
+ rake command:
121
+
122
+ `rake remy:server:create[foobar.sharespost.com,4]`
123
+
124
+ [See here](http://obn.me/2011/04/rackspace-cloud-images-and-flavors-id/) for a list of Rackspace flavor IDs and image
125
+ IDs (they might differ for other cloud providers).
126
+
127
+ ### Bootstrap your new cloud server to run Remy and chef-solo
128
+
129
+ This bootstrap task installs the prerequisites necessary to run chef on this new remote box, such as updating the Linux
130
+ distribution, installing RVM, installing the Chef gem and its prerequisites, and installing rspec (which is required
131
+ because we do test-driven Chef):
132
+
133
+ `rake remy:chef:bootstrap[:ip_address, :password]`
134
+
135
+
136
+ ### Build and bootstrap your cloud server
137
+
138
+ You can both build a box and bootstrap it for chef-solo all in one step:
139
+
140
+ `rake remy:server:create_and_bootstrap[:server_name, :flavor_id, :cloud_api_key, :cloud_username, :cloud_provider, :image_id]`
141
+
142
+ ### Run chef-solo
143
+
144
+ `rake remy:chef:run['123.123.123.123']`
145
+
146
+ will run chef-solo on the remote host at 123.123.123.123. You can also specify the rake arguments as properties:
147
+
148
+ `rake remy:chef:run['ip_address:123.123.123.123']`
149
+
150
+ Run Remy against a collection of remote nodes:
151
+
152
+ `rake remy:chef:run['rails_env:staging role:app']`
153
+
154
+ This will run Remy against all app servers for the staging environment; i.e., all servers in the :servers section of the Remy config files
155
+ that match :rails_env = :staging and :role = :app will have Remy (i.e., chef-solo) run on them. Run Remy against a
156
+ specific named node:
157
+
158
+ `rake remy:chef:run['demo.sharespost.com']`
159
+
160
+ This assumes that 'demo.sharespost.com' is specified in the :servers section of a Remy yml config file.
161
+
162
+
163
+ ## Remy usage from Ruby code (i.e., from within Capistrano or elsewhere)
164
+
165
+ ### Run chef-solo on a single box:
166
+
167
+ The simplest usage of Remy (Note: this only works if the Remy.configuration has already been specified, such as within
168
+ a Rails application):
169
+
170
+ `Remy::Chef.new(:ip_address => '123.123.123.123').run`
171
+
172
+ Note that the :ip_address could also have been specified in the yml config files, although more commonly it's passed in
173
+ as an argument.
174
+
175
+ Example: update your production database box:
176
+
177
+ server_config = Remy.find_server_config(:rails_env => :production, :role => :db)
178
+ Remy::Chef.new(server_config).run
179
+
180
+ Other arguments can be passed into chef and will get applied against this node:
181
+
182
+ `Remy::Chef.new(:ip_address => '123.123.123.123', :color => :blue, :height => :short).run`
183
+
184
+ means that you can access node['color'] and node['height'] from within your Chef recipes, which will be :blue and :short,
185
+ respectively. You can also give arguments which will be passed to chef-solo:
186
+
187
+ `Remy::Chef.new(:ip_address => '123.123.123.123', :chef_arguments => '-l debug').run`
188
+
189
+ which will make chef-solo run in debug mode.
190
+
191
+
192
+ ### Run chef-solo on a collection of boxes:
193
+
194
+ From within your Capistrano file, you do a variety of things, such as the following:
195
+
196
+ Remy.servers.find_servers(:rails_env => :staging, :role => :app) do |server|
197
+ Remy::Chef.new(server).run
198
+ end
199
+
200
+ ## Chef location on the remote box
201
+
202
+ The chef files are installed in /var/chef by default (this can be overridden to be another location in the
203
+ [Remy configuration](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/config/initializers/remy.rb)).
204
+
205
+ ### Running chef-solo manually on the remote box
206
+
207
+ You can always ssh into the remote box, cd into the chef directory (/var/chef by default; it could have been overridden in the
208
+ Remy configuration), and run chef-solo by typing
209
+
210
+ `/var/chef/run_chef_solo`
211
+
212
+ as root. You can pass in debug arguments for chef-solo, e.g.:
213
+
214
+ `/var/chef/run_chef_solo -l debug`
215
+
216
+ If you ssh to the remote box, you can see the glob of JSON that was created by blending together all of the various
217
+ Remy yml config files at /var/chef/node.json.
218
+
219
+
220
+
221
+ ## Test-Driven Chef (TDC)
222
+
223
+ Look at the example specs in [chef/spec](http://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/spec) to see how we do
224
+ Test-Driven Chef (TDC). When writing our recipes, we first write a failing spec, and then write the Chef recipe to
225
+ make this test pass; we then refactor the recipe as needed. Each time Remy (i.e., chef-solo) is run on a remote box, the
226
+ specs for this recipe are also run. As an example of this, see the
227
+ [bottom of this recipe](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/cookbooks/server_bootstrap/recipes/create_user.rb)
228
+ for where spot where the specs are invoked for the Chef recipe 'create_user'. The
229
+ [specs for this recipe are here](https://github.com/gregwoodward/remy_cluster_rails_example/blob/master/chef/spec/server_bootstrap/create_user_spec.rb)
230
+ and consist of the various things that one would want to check for when this recipe executes. Note that these specs run on the remote box,
231
+ not the local box.
232
+
233
+ ## License
234
+
235
+ Remy is released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Dir['lib/tasks/**/*.rake'].each {|file| load file }
5
+
6
+ desc 'Run specs'
7
+ RSpec::Core::RakeTask.new do |t|
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,13 @@
1
+ template "/tmp/hello_world.txt" do
2
+ source "hello_world.txt.erb"
3
+ owner 'root'
4
+ group 'staff'
5
+ mode "644"
6
+ variables(:color => node['color'], :ip_address => node['ip_address'], :rails_env => node['rails_env'])
7
+ end
8
+
9
+ ruby_block "test" do
10
+ block do
11
+ puts `rspec -f progress #{File.expand_path(File.join(File.dirname(__FILE__), '../../../spec/hello_world/default_spec.rb'))}`
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ hello, world! I am feeling <%= @color %>!
2
+ My rails env is <%= @rails_env %>
3
+ My IP is <%= @ip_address %>
4
+
5
+ Node info: <%= node.to_yaml %>
6
+
@@ -0,0 +1,3 @@
1
+ name 'test_role'
2
+ description 'This is a test role'
3
+ run_list 'recipe[hello_world]'
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "test_role2",
3
+ "default_attributes": { },
4
+ "override_attributes": { },
5
+ "json_class": "Chef::Role",
6
+ "description": "A 2nd test role",
7
+ "chef_type": "role",
8
+ "run_list": [ "recipe[hello_world]" ]
9
+ }
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+
3
+ describe 'hello_world/default' do
4
+ it { File.exist?('/tmp/hello_world.txt').should be_true }
5
+ it { File.read('/tmp/hello_world.txt').should include('blue') }
6
+ end
@@ -0,0 +1,82 @@
1
+ module Remy
2
+ class Bootstrap
3
+ include ::Remy::Shell
4
+
5
+ attr_reader :ip_address, :ruby_version, :password, :gems
6
+
7
+ def initialize(options = { })
8
+ @ip_address = options[:ip_address]
9
+ @password = options[:password]
10
+ options = (Remy.bootstrap || {}).merge(options).symbolize_keys
11
+ @ruby_version = options[:ruby_version] || '1.8.7'
12
+ @gems = options[:gems] || {}
13
+ @quiet = options[:quiet] || false
14
+ end
15
+
16
+ def run
17
+ copy_ssh_key_to_remote
18
+ update_linux_distribution
19
+ install_rvm
20
+ install_gems_to_bootstrap_chef
21
+ end
22
+
23
+ private
24
+
25
+ def apt_get_rvm_packages
26
+ # This list of required packages came from doing "rvm requirements"
27
+ remote_apt_get 'build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison'
28
+ end
29
+
30
+ def install_gems_to_bootstrap_chef
31
+ remote_gem 'bundler', :version => gems[:bundler]
32
+ remote_gem 'chef', :version => gems[:chef]
33
+ remote_gem 'rspec', :version => gems[:rspec] # Required because we do Test-Driven Chef (TDC)!
34
+ end
35
+
36
+ def install_rvm
37
+ remote_execute rvm_multi_user_install
38
+ apt_get_rvm_packages
39
+ remote_execute "/usr/local/rvm/bin/rvm install #{ruby_version}"
40
+ remote_execute "/usr/local/rvm/bin/rvm #{ruby_version} --default"
41
+ end
42
+
43
+ def rvm_multi_user_install
44
+ 'curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer -o rvm-installer ; chmod +x rvm-installer ; sudo -s ./rvm-installer --version latest'
45
+ end
46
+
47
+ def is_ssh_copy_id_installed_locally?
48
+ `which ssh-copy-id`.length > 0
49
+ end
50
+
51
+ def copy_ssh_key_to_remote
52
+ raise "ssh-copy-id is not installed locally! On the Mac, do 'brew install ssh-copy-id'" unless is_ssh_copy_id_installed_locally?
53
+ is_ssh_key_in_local_known_hosts_file = `grep "#{ip_address}" ~/.ssh/known_hosts`.length > 0
54
+ if is_ssh_key_in_local_known_hosts_file
55
+ execute %Q{expect -c 'spawn ssh-copy-id #{user}@#{ip_address}; expect assword ; send "#{password}\\n" ; interact'}
56
+ else
57
+ execute %Q{expect -c 'spawn ssh-copy-id #{user}@#{ip_address}; expect continue; send "yes\\n"; expect assword ; send "#{password}\\n" ; interact'}
58
+ end
59
+ end
60
+
61
+ def quiet?
62
+ @quiet
63
+ end
64
+
65
+ def remote_gem(gem_name, options={ })
66
+ if options[:version]
67
+ version = options[:version]
68
+ raise ArgumentError.new unless version.match(/^\d[.\d]+\d/)
69
+ version_info = "-v #{version}"
70
+ end
71
+ remote_execute "/usr/local/rvm/bin/gem install #{gem_name} #{version_info} --no-rdoc --no-ri"
72
+ end
73
+
74
+ def remote_apt_get(package_name)
75
+ remote_execute "apt-get install -y #{package_name}"
76
+ end
77
+
78
+ def update_linux_distribution
79
+ remote_execute 'apt-get update && apt-get --yes upgrade'
80
+ end
81
+ end
82
+ end
data/lib/remy/chef.rb ADDED
@@ -0,0 +1,135 @@
1
+ module Remy
2
+ class Chef
3
+ # For chef-solo info, see: http://wiki.opscode.com/display/chef/Chef+Solo
4
+ include ::Remy::Shell
5
+ include FileUtils
6
+ attr_reader :ip_address
7
+
8
+ def initialize(options = {})
9
+ options = JSON.parse(options).symbolize_keys! if options.is_a?(String)
10
+ @chef_args = options.delete(:chef_args)
11
+ @quiet = options.delete(:quiet)
12
+ @node_configuration = Remy.configuration.dup
13
+ @ip_address = options[:ip_address] ? options[:ip_address] : @node_configuration.ip_address
14
+ server_config = Remy.find_server_config(:ip_address => ip_address) || Hashie::Mash.new
15
+ @node_configuration.deep_merge!(server_config)
16
+ @node_configuration.merge!(options)
17
+ end
18
+
19
+ def run
20
+ create_temp_dir_which_contains_cookbooks_roles_and_scripts
21
+ rsync_temp_dir_with_cookbooks_to_remote_host
22
+ run_chef_solo_on_remote_host
23
+ end
24
+
25
+ def self.rake_run(rake_options)
26
+ ip_addresses = Remy.determine_ip_addresses_for_remy_run(rake_options)
27
+ ip_addresses.each do |ip_address|
28
+ Remy::Chef.new(:ip_address => ip_address).run
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def create_temp_dir_which_contains_cookbooks_roles_and_scripts
35
+ create_temp_dir
36
+ copy_spec_cookbook_and_role_dirs_to_tmp_dir
37
+ create_solo_rb
38
+ create_bash_script_which_runs_chef
39
+ create_node_json_from_node_configuration
40
+ end
41
+
42
+ def rsync_temp_dir_with_cookbooks_to_remote_host
43
+ remote_execute "mkdir -p #{remote_chef_dir}"
44
+ olddir = pwd
45
+ begin
46
+ chdir(tmp_dir)
47
+ execute "rsync -a #{rsync_delete_flag} * #{user}@#{ip_address}:#{remote_chef_dir}"
48
+ ensure
49
+ chdir(olddir)
50
+ end
51
+ end
52
+
53
+ def run_chef_solo_on_remote_host
54
+ remote_execute "bash --login -c #{remote_chef_dir}/#{run_chef_solo_bash_script}"
55
+ end
56
+
57
+ def create_temp_dir
58
+ @tmpdir = Dir.mktmpdir
59
+ end
60
+
61
+ def rsync_delete_flag
62
+ if remote_execute("test -e #{remote_chef_dir}")
63
+ chef_dir_contains_valid_chef_content = remote_execute("test -e #{remote_chef_dir}/#{node_json} && test -e #{remote_chef_dir}/#{solo_rb}")
64
+ chef_dir_contains_valid_chef_content ? '--delete' : nil
65
+ end
66
+ end
67
+
68
+ def copy_spec_cookbook_and_role_dirs_to_tmp_dir
69
+ [@node_configuration.roles_path, @node_configuration.cookbook_path, @node_configuration.spec_path].each do |path|
70
+ if path
71
+ full_path = path.map{|p| File.expand_path(p) }
72
+ full_path.each do |a_path|
73
+ cp_r a_path, tmp_dir
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def create_solo_rb
80
+ solo_rb_contents = <<-EOF
81
+ file_cache_path "#{remote_chef_dir}"
82
+ cookbook_path ["#{remote_chef_dir}/cookbooks"]
83
+ role_path "#{remote_chef_dir}/roles"
84
+ cache_options({ :path => "#{remote_chef_dir}/cache/checksums", :skip_expires => true })
85
+ EOF
86
+ File.open(File.join(tmp_dir, solo_rb), 'w+') do |f|
87
+ f << solo_rb_contents
88
+ end
89
+ end
90
+
91
+ def create_bash_script_which_runs_chef
92
+ run_chef = <<-EOF
93
+ #!/bin/bash
94
+ # Pass "-l debug" to this script to get more debug output
95
+
96
+ # $@ gets the array of args from Bash
97
+ chef-solo $@ #{@chef_args} -j #{remote_chef_dir}/#{node_json} -c #{remote_chef_dir}/#{solo_rb}
98
+ EOF
99
+ File.open(File.join(tmp_dir, run_chef_solo_bash_script), 'w+') do |f|
100
+ f << run_chef
101
+ end
102
+ chmod(0755, File.join(tmp_dir, run_chef_solo_bash_script))
103
+ end
104
+
105
+ def create_node_json_from_node_configuration
106
+ File.open(File.join(tmp_dir, node_json), 'w+') do |f|
107
+ f << @node_configuration.to_json
108
+ end
109
+ end
110
+
111
+ def node_json
112
+ 'node.json'
113
+ end
114
+
115
+ def solo_rb
116
+ 'solo.rb'
117
+ end
118
+
119
+ def remote_chef_dir
120
+ @node_configuration.remote_chef_dir
121
+ end
122
+
123
+ def tmp_dir
124
+ File.expand_path(@tmpdir)
125
+ end
126
+
127
+ def run_chef_solo_bash_script
128
+ 'run_chef_solo'
129
+ end
130
+
131
+ def quiet?
132
+ @quiet
133
+ end
134
+ end
135
+ end