yyuu-capistrano-chef-solo 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +38 -0
- data/README.md +3 -0
- data/capistrano-chef-solo.gemspec +1 -1
- data/lib/capistrano-chef-solo.rb +73 -33
- data/lib/capistrano-chef-solo/version.rb +1 -1
- data/test/config/deploy.rb +49 -18
- metadata +9 -2
data/CHANGES.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
v0.1.0 (Yamashita, Yuu)
|
2
|
+
|
3
|
+
* Deploy cookbooks from local `./config/cookbooks` by default. Also you can still deploy them from external SCM with some extra configuration.
|
4
|
+
* Update default Chef version (10.16.4 -> 11.4.0)
|
5
|
+
* Add `chef_solo.run_list()` method like [roundsman](https://github.com/iain/roundsman).
|
6
|
+
* Add `chef_solo:attributes` task to debug attributes.
|
7
|
+
* Replace old style `:chef_solo_user` by _bootstrap_ mode.
|
8
|
+
* Call `bundle install` to install Chef only if the Chef has not been installed.
|
9
|
+
|
10
|
+
v0.1.1 (Yamashita, Yuu)
|
11
|
+
|
12
|
+
* Set `:rbenv_setup_default_environment` as `false` to avoid the problem with _bootstrap_ mode.
|
13
|
+
|
14
|
+
v0.1.2 (Yamashita, Yuu)
|
15
|
+
|
16
|
+
* Deploy cookbooks with using deploy strategies of Capistrano. Use [copy_subdir](https://github.com/yyuu/capistrano-copy-subdir) strategy by default.
|
17
|
+
|
18
|
+
v0.1.3 (Yamashita, Yuu)
|
19
|
+
|
20
|
+
* Add support for deploying single cookbook repositories (like cookbooks in https://github.com/opscode-cookbooks).
|
21
|
+
|
22
|
+
v0.1.4 (Yamashita, Yuu)
|
23
|
+
|
24
|
+
* Add support for data bags of Chef. By default, deploy data bags from local `./config/data_bags`.
|
25
|
+
* Add `:chef_solo_use_bundler` parameter. Try to install Chef without using Bundler if set `false`.
|
26
|
+
* Add `chef_solo:purge` task to uninstall Chef.
|
27
|
+
|
28
|
+
v0.1.5 (Yamashita, Yuu)
|
29
|
+
|
30
|
+
* Catch exceptions on initial handshake during bootstrap mode.
|
31
|
+
* Improve error logging during bootstrap mode.
|
32
|
+
* Support regex match for `:chef_solo_capistrano_attributes_exclude` and `:chef_solo_capistrano_attributes_include`.
|
33
|
+
|
34
|
+
v0.1.6 (Yamashita, Yuu)
|
35
|
+
|
36
|
+
* Support options Hash in `chef_solo.run_list()`. Now it skips updating cookbooks, data bags, attirbutes and configurations if `:update => false` was given.
|
37
|
+
* Generate Chef roles from Capistrano roles and apply them by default.
|
38
|
+
* Add new tasks chef-solo:host_attributes and chef-solo:role_attributes to show effective attributes.
|
data/README.md
CHANGED
@@ -181,6 +181,9 @@ set(:chef_solo_run_list, ["recipe[locales]", "recipe[tzdata]"])
|
|
181
181
|
|
182
182
|
In some cases, you may want to apply individual `attributes` per roles.
|
183
183
|
|
184
|
+
Following example will create Chef roles `role[app]` and `role[web]`.
|
185
|
+
All attributes defined in `:chef_solo_role_attributes` will be merged into `default_attributes` of Chef role.
|
186
|
+
|
184
187
|
```ruby
|
185
188
|
set(:chef_solo_role_attributes) {
|
186
189
|
:app => {
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = Capistrano::ChefSolo::VERSION
|
18
18
|
|
19
|
-
gem.add_dependency("capistrano", ">= 2.10.0")
|
19
|
+
gem.add_dependency("capistrano", [">= 2.10.0", "< 3"])
|
20
20
|
gem.add_dependency("capistrano-copy-subdir", ">= 0.1.0")
|
21
21
|
gem.add_dependency("capistrano-rbenv", ">= 1.0.1")
|
22
22
|
end
|
data/lib/capistrano-chef-solo.rb
CHANGED
@@ -35,6 +35,16 @@ module Capistrano
|
|
35
35
|
task(:attributes, :except => { :no_release => true }) {
|
36
36
|
find_and_execute_task("chef_solo:attributes")
|
37
37
|
}
|
38
|
+
|
39
|
+
desc("Show chef-solo attributes for role. (an alias of chef_solo:role_attributes)")
|
40
|
+
task(:role_attributes, :except => { :no_release => true }) {
|
41
|
+
find_and_execute_task("chef_solo:role_attributes")
|
42
|
+
}
|
43
|
+
|
44
|
+
desc("Show chef-solo attributes for host. (an alias of chef_solo:host_attributes)")
|
45
|
+
task(:host_attributes, :except => { :no_release => true }) {
|
46
|
+
find_and_execute_task("chef_solo:host_attributes")
|
47
|
+
}
|
38
48
|
}
|
39
49
|
|
40
50
|
namespace(:chef_solo) {
|
@@ -45,6 +55,7 @@ module Capistrano
|
|
45
55
|
_cset(:chef_solo_config_path) { File.join(chef_solo_path, "config") }
|
46
56
|
_cset(:chef_solo_cookbooks_path) { File.join(chef_solo_path, "cookbooks") }
|
47
57
|
_cset(:chef_solo_data_bags_path) { File.join(chef_solo_path, "data_bags") }
|
58
|
+
_cset(:chef_solo_roles_path) { File.join(chef_solo_path, "roles") }
|
48
59
|
_cset(:chef_solo_config_file) { File.join(chef_solo_config_path, "solo.rb") }
|
49
60
|
_cset(:chef_solo_attributes_file) { File.join(chef_solo_config_path, "solo.json") }
|
50
61
|
|
@@ -175,11 +186,13 @@ module Capistrano
|
|
175
186
|
|
176
187
|
# Acts like `default`, but will apply specified recipes only.
|
177
188
|
def run_list(*recipes)
|
189
|
+
options = { :update => true }.merge(Hash === recipes.last ? recipes.pop : {})
|
190
|
+
update_p = options.delete(:update)
|
178
191
|
connect_with_settings do
|
179
192
|
setup
|
180
193
|
transaction do
|
181
|
-
update
|
182
|
-
invoke(:run_list => recipes)
|
194
|
+
update(options) if update_p
|
195
|
+
invoke(options.merge(:run_list => recipes))
|
183
196
|
end
|
184
197
|
end
|
185
198
|
end
|
@@ -195,11 +208,34 @@ module Capistrano
|
|
195
208
|
|
196
209
|
desc("Show chef-solo attributes.")
|
197
210
|
task(:attributes, :except => { :no_release => true }) {
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
211
|
+
find_and_execute_task("chef_solo:host_attributes")
|
212
|
+
find_and_execute_task("chef_solo:role_attributes")
|
213
|
+
}
|
214
|
+
|
215
|
+
desc("Show chef-solo attributes for host.")
|
216
|
+
task(:host_attributes, :except => { :no_release => true }) {
|
217
|
+
if ENV.key?("HOST")
|
218
|
+
hosts = ENV.fetch("HOST", "").split(/\s*,\s*/)
|
219
|
+
else
|
220
|
+
hosts = find_servers_for_task(current_task).map { |server| server.host }
|
221
|
+
end
|
222
|
+
hosts.each do |host|
|
223
|
+
logger.debug("generating chef-solo attributes for host `#{host}'.")
|
224
|
+
STDOUT.puts(_json_attributes(_generate_host_attributes(host, :roles => role_names_for_host(ServerDefinition.new(host)))))
|
225
|
+
end
|
226
|
+
}
|
227
|
+
|
228
|
+
desc("Show chef-solo attributes for role.")
|
229
|
+
task(:role_attributes, :except => { :no_release => true }) {
|
230
|
+
if ENV.key?("ROLE")
|
231
|
+
roles = ENV.fetch("ROLE", "").split(/\s*,\s*/).map { |role| role.to_sym }
|
232
|
+
else
|
233
|
+
roles = find_servers_for_task(current_task).map { |server| role_names_for_host(server) }.flatten.uniq
|
234
|
+
end
|
235
|
+
roles.each do |role|
|
236
|
+
logger.debug("generating chef-solo attributes for role `#{role}'.")
|
237
|
+
STDOUT.puts(_json_attributes(_generate_role_attributes(role)))
|
238
|
+
end
|
203
239
|
}
|
204
240
|
|
205
241
|
_cset(:chef_solo_gem_dependencies) {{
|
@@ -226,9 +262,9 @@ module Capistrano
|
|
226
262
|
rescue
|
227
263
|
installed = false
|
228
264
|
end
|
265
|
+
dirs = [ chef_solo_path, chef_solo_cache_path, chef_solo_config_path, chef_solo_roles_path ].uniq
|
266
|
+
run("mkdir -p #{dirs.map { |x| x.dump }.join(" ")}")
|
229
267
|
unless installed
|
230
|
-
dirs = [ chef_solo_path, chef_solo_cache_path, chef_solo_config_path ].uniq
|
231
|
-
run("mkdir -p #{dirs.map { |x| x.dump }.join(" ")}")
|
232
268
|
if chef_solo_use_bundler
|
233
269
|
top.put(chef_solo_gemfile, File.join(chef_solo_path, "Gemfile"))
|
234
270
|
args = fetch(:chef_solo_bundle_options, [])
|
@@ -423,6 +459,7 @@ module Capistrano
|
|
423
459
|
file_cache_path #{chef_solo_cache_path.dump}
|
424
460
|
cookbook_path #{chef_solo_cookbooks_path.dump}
|
425
461
|
data_bag_path #{chef_solo_data_bags_path.dump}
|
462
|
+
role_path #{chef_solo_roles_path.dump}
|
426
463
|
EOS
|
427
464
|
}
|
428
465
|
def update_config(options={})
|
@@ -469,50 +506,53 @@ module Capistrano
|
|
469
506
|
])
|
470
507
|
_cset(:chef_solo_capistrano_attributes_exclude, [:logger, /password/, :source, :strategy])
|
471
508
|
_cset(:chef_solo_attributes, {})
|
472
|
-
_cset(:chef_solo_host_attributes, {})
|
473
|
-
_cset(:chef_solo_role_attributes, {})
|
474
509
|
_cset(:chef_solo_run_list, [])
|
510
|
+
_cset(:chef_solo_host_attributes, {})
|
475
511
|
_cset(:chef_solo_host_run_list, {})
|
476
|
-
|
477
|
-
|
478
|
-
def _generate_attributes(options={})
|
479
|
-
hosts = [ options.delete(:hosts) ].flatten.compact.uniq
|
512
|
+
def _generate_host_attributes(host, options={})
|
480
513
|
roles = [ options.delete(:roles) ].flatten.compact.uniq
|
481
514
|
#
|
482
515
|
# By default, the Chef attributes will be generated by following order.
|
483
516
|
#
|
484
517
|
# 1. Use _non-lazy_ variables of Capistrano.
|
485
518
|
# 2. Use attributes defined in `:chef_solo_attributes`.
|
486
|
-
# 3. Use attributes defined in `:
|
487
|
-
# 4. Use attributes defined in `:chef_solo_host_attributes` for target host.
|
519
|
+
# 3. Use attributes defined in `:chef_solo_host_attributes` for target host.
|
488
520
|
#
|
489
521
|
attributes = chef_solo_capistrano_attributes.dup
|
490
522
|
_merge_attributes!(attributes, chef_solo_attributes)
|
491
|
-
|
492
|
-
_merge_attributes!(attributes, chef_solo_role_attributes.fetch(role, {}))
|
493
|
-
end
|
494
|
-
hosts.each do |host|
|
495
|
-
_merge_attributes!(attributes, chef_solo_host_attributes.fetch(host, {}))
|
496
|
-
end
|
523
|
+
_merge_attributes!(attributes, chef_solo_host_attributes.fetch(host, {}))
|
497
524
|
#
|
498
|
-
# The Chef `run_list` will be generated from `:
|
499
|
-
# `:chef_solo_host_run_list`.
|
525
|
+
# The Chef `run_list` will be generated from `:chef_solo_run_list` and `:chef_solo_host_run_list`.
|
500
526
|
#
|
501
527
|
_merge_attributes!(attributes, {"run_list" => chef_solo_run_list})
|
502
|
-
roles.
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
528
|
+
_merge_attributes!(attributes, {"run_list" => roles.map { |role| "role[#{role}]" } }) unless roles.empty?
|
529
|
+
_merge_attributes!(attributes, {"run_list" => chef_solo_host_run_list.fetch(host, [])})
|
530
|
+
attributes
|
531
|
+
end
|
532
|
+
|
533
|
+
_cset(:chef_solo_role_attributes, {})
|
534
|
+
_cset(:chef_solo_role_run_list, {})
|
535
|
+
def _generate_role_attributes(role, options={})
|
536
|
+
attributes = {
|
537
|
+
"name" => role,
|
538
|
+
"chef_type" => "role",
|
539
|
+
"json_class" => "Chef::Role",
|
540
|
+
}
|
541
|
+
_merge_attributes!(attributes, {"default_attributes" => chef_solo_role_attributes.fetch(role, {})})
|
542
|
+
_merge_attributes!(attributes, {"run_list" => chef_solo_role_run_list.fetch(role, [])})
|
508
543
|
attributes
|
509
544
|
end
|
510
545
|
|
511
546
|
def update_attributes(options={})
|
547
|
+
roles.each_key.each do |role|
|
548
|
+
logger.debug("updating chef-solo role attributes for #{role}.")
|
549
|
+
attributes = _generate_role_attributes(role)
|
550
|
+
top.put(_json_attributes(attributes), File.join(chef_solo_roles_path, "#{role}.json"), options)
|
551
|
+
end
|
512
552
|
servers = find_servers_for_task(current_task)
|
513
553
|
servers.each do |server|
|
514
|
-
logger.debug("updating chef-solo attributes for #{server.host}.")
|
515
|
-
attributes =
|
554
|
+
logger.debug("updating chef-solo host attributes for #{server.host}.")
|
555
|
+
attributes = _generate_host_attributes(server.host, :roles => role_names_for_host(server))
|
516
556
|
top.put(_json_attributes(attributes), chef_solo_attributes_file, options.merge(:hosts => server.host))
|
517
557
|
end
|
518
558
|
end
|
data/test/config/deploy.rb
CHANGED
@@ -39,22 +39,52 @@ def get_file(file, options={})
|
|
39
39
|
tempfile.read
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def get_host_attributes(options={})
|
43
43
|
JSON.load(get_file(chef_solo_attributes_file, options))
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
47
|
-
|
46
|
+
def get_role_attributes(role, options={})
|
47
|
+
JSON.load(get_file(File.join(chef_solo_roles_path, "#{role}.json"), options))
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_attribute(x, y)
|
51
|
+
if Array === x
|
52
|
+
x.sort == Array(y).sort
|
53
|
+
else
|
54
|
+
x == y
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def assert_host_attributes(expected, options={})
|
59
|
+
found = get_host_attributes(options)
|
48
60
|
expected.each do |key, value|
|
49
|
-
|
50
|
-
abort("invalid attribute: #{key.inspect} (expected:#{value.inspect} != found:#{found[key].inspect})")
|
61
|
+
unless test_attribute(found[key], value)
|
62
|
+
abort("invalid host attribute: #{key.inspect} (expected:#{value.inspect} != found:#{found[key].inspect})")
|
51
63
|
end
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
55
|
-
def
|
56
|
-
found =
|
57
|
-
|
67
|
+
def assert_role_attributes(role, expected, options={})
|
68
|
+
found = get_role_attributes(role, options)
|
69
|
+
expected.each do |key, value|
|
70
|
+
unless test_attribute(found[key], value)
|
71
|
+
abort("invalid role attribute: #{key.inspect} (expected:#{value.inspect} != found:#{found[key].inspect})")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def assert_host_run_list(expected, options={})
|
77
|
+
found = get_host_attributes(options)["run_list"]
|
78
|
+
unless test_attribute(found, expected)
|
79
|
+
abort("invalid host run_list (expected:#{expected.inspect} != found:#{found.inspect})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def assert_role_run_list(role, expected=[], options={})
|
84
|
+
found = [ get_role_attributes(role, options)["run_list"] ].flatten
|
85
|
+
unless test_attribute(found, expected)
|
86
|
+
abort("invalid role run_list (expected:#{expected.inspect} != found:#{found.inspect})")
|
87
|
+
end
|
58
88
|
end
|
59
89
|
|
60
90
|
def assert_file_exists(file, options={})
|
@@ -149,8 +179,9 @@ namespace(:test_default) {
|
|
149
179
|
|
150
180
|
task(:test_attributes, :roles => :app) {
|
151
181
|
chef_solo.update_attributes
|
152
|
-
|
153
|
-
|
182
|
+
assert_host_attributes({"aaa" => "AAA", "ccc" => "CCC", "run_list" => %w(recipe[foo] recipe[baz] role[web] role[app] role[db])})
|
183
|
+
assert_role_attributes(:app, {"default_attributes" => {"bbb" => "BBB"}, "run_list" => %w(recipe[bar])})
|
184
|
+
assert_host_attributes({"application" => application, "deploy_to" => deploy_to})
|
154
185
|
}
|
155
186
|
}
|
156
187
|
|
@@ -189,14 +220,14 @@ namespace(:test_without_bundler) {
|
|
189
220
|
task(:test_invoke) {
|
190
221
|
expected = chef_solo_run_list
|
191
222
|
find_and_execute_task("chef-solo")
|
192
|
-
|
223
|
+
assert_host_run_list(expected + %w(role[web] role[app] role[db]))
|
193
224
|
_test_recipes(expected)
|
194
225
|
}
|
195
226
|
|
196
227
|
task(:test_run_list) {
|
197
228
|
expected = %w(recipe[baz])
|
198
229
|
chef_solo.run_list expected
|
199
|
-
#
|
230
|
+
# assert_host_run_list(expected + %w(role[web] role[app] role[db])) # arguments of chef_solo.run_list will not be written to attributes file
|
200
231
|
_test_recipes(expected)
|
201
232
|
}
|
202
233
|
}
|
@@ -228,14 +259,14 @@ namespace(:test_with_local_cookbooks) {
|
|
228
259
|
task(:test_invoke) {
|
229
260
|
expected = chef_solo_run_list
|
230
261
|
find_and_execute_task("chef-solo")
|
231
|
-
|
262
|
+
assert_host_run_list(expected + %w(role[web] role[app] role[db]))
|
232
263
|
_test_recipes(expected)
|
233
264
|
}
|
234
265
|
|
235
266
|
task(:test_run_list) {
|
236
267
|
expected = %w(recipe[baz])
|
237
268
|
chef_solo.run_list expected
|
238
|
-
#
|
269
|
+
# assert_host_run_list(expected + %w(role[web] role[app] role[db])) # arguments of chef_solo.run_list will not be written to attributes file
|
239
270
|
_test_recipes(expected)
|
240
271
|
}
|
241
272
|
}
|
@@ -269,14 +300,14 @@ namespace(:test_with_remote_cookbooks) {
|
|
269
300
|
task(:test_invoke) {
|
270
301
|
expected = chef_solo_run_list
|
271
302
|
find_and_execute_task("chef-solo")
|
272
|
-
|
303
|
+
assert_host_run_list(expected + %w(role[web] role[app] role[db]))
|
273
304
|
_test_recipes(expected)
|
274
305
|
}
|
275
306
|
|
276
307
|
task(:test_run_list) {
|
277
308
|
expected = %w(recipe[three])
|
278
309
|
chef_solo.run_list expected
|
279
|
-
#
|
310
|
+
# assert_host_run_list(expected + %w(role[web] role[app] role[db])) # arguments of chef_solo.run_list will not be written to attributes file
|
280
311
|
_test_recipes(expected)
|
281
312
|
}
|
282
313
|
}
|
@@ -340,14 +371,14 @@ namespace(:test_with_multiple_cookbooks) {
|
|
340
371
|
task(:test_invoke) {
|
341
372
|
expected = chef_solo_run_list
|
342
373
|
find_and_execute_task("chef-solo")
|
343
|
-
|
374
|
+
assert_host_run_list(expected + %w(role[web] role[app] role[db]))
|
344
375
|
_test_recipes(expected)
|
345
376
|
}
|
346
377
|
|
347
378
|
task(:test_run_list) {
|
348
379
|
expected = %w(recipe[foo] recipe[single] recipe[one])
|
349
380
|
chef_solo.run_list expected
|
350
|
-
#
|
381
|
+
# assert_host_run_list(expected + %w(role[web] role[app] role[db])) # arguments of chef_solo.run_list will not be written to attributes file
|
351
382
|
_test_recipes(expected)
|
352
383
|
}
|
353
384
|
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yyuu-capistrano-chef-solo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: capistrano
|
@@ -19,6 +19,9 @@ dependencies:
|
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 2.10.0
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '3'
|
22
25
|
type: :runtime
|
23
26
|
prerelease: false
|
24
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,6 +30,9 @@ dependencies:
|
|
27
30
|
- - ! '>='
|
28
31
|
- !ruby/object:Gem::Version
|
29
32
|
version: 2.10.0
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '3'
|
30
36
|
- !ruby/object:Gem::Dependency
|
31
37
|
name: capistrano-copy-subdir
|
32
38
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,6 +73,7 @@ extensions: []
|
|
67
73
|
extra_rdoc_files: []
|
68
74
|
files:
|
69
75
|
- .gitignore
|
76
|
+
- CHANGES.md
|
70
77
|
- Gemfile
|
71
78
|
- LICENSE.txt
|
72
79
|
- README.md
|