remy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +235 -0
- data/Rakefile +10 -0
- data/chef/cookbooks/hello_world/recipes/default.rb +13 -0
- data/chef/cookbooks/hello_world/templates/default/hello_world.txt.erb +6 -0
- data/chef/roles/test_role.rb +3 -0
- data/chef/roles/test_role2.json +9 -0
- data/chef/spec/hello_world/default_spec.rb +6 -0
- data/lib/remy/bootstrap.rb +82 -0
- data/lib/remy/chef.rb +135 -0
- data/lib/remy/remy.rb +140 -0
- data/lib/remy/server.rb +71 -0
- data/lib/remy/shell.rb +26 -0
- data/lib/remy/version.rb +3 -0
- data/lib/remy.rb +56 -0
- data/lib/tasks/remy.rake +51 -0
- data/remy.gemspec +31 -0
- data/spec/fixtures/bar.yml +7 -0
- data/spec/fixtures/chef.yml +44 -0
- data/spec/fixtures/foo.yml +5 -0
- data/spec/fixtures/hello_world_chef.yml +8 -0
- data/spec/remy/bootstrap_spec.rb +61 -0
- data/spec/remy/chef_spec.rb +82 -0
- data/spec/remy/integration/chef_spec.rb +41 -0
- data/spec/remy_spec.rb +330 -0
- data/spec/spec_helper.rb +16 -0
- metadata +230 -0
data/.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
|