remy 0.0.1

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