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/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7-p352@remy --create
|
data/Gemfile
ADDED
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,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,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
|