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.
@@ -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
@@ -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
- hosts = ENV.fetch("HOST", "").split(/\s*,\s*/)
199
- roles = ENV.fetch("ROLE", "").split(/\s*,\s*/).map { |role| role.to_sym }
200
- roles += hosts.map { |host| role_names_for_host(ServerDefinition.new(host)) }
201
- attributes = _generate_attributes(:hosts => hosts, :roles => roles)
202
- STDOUT.puts(_json_attributes(attributes))
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
- _cset(:chef_solo_role_run_list, {})
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 `:chef_solo_role_attributes` for target role.
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
- roles.each do |role|
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 `:chef_solo_role_run_list` and
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.each do |role|
503
- _merge_attributes!(attributes, {"run_list" => chef_solo_role_run_list.fetch(role, [])})
504
- end
505
- hosts.each do |host|
506
- _merge_attributes!(attributes, {"run_list" => chef_solo_host_run_list.fetch(host, [])})
507
- end
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 = _generate_attributes(:hosts => server.host, :roles => role_names_for_host(server))
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
@@ -1,5 +1,5 @@
1
1
  module Capistrano
2
2
  module ChefSolo
3
- VERSION = "0.1.5"
3
+ VERSION = "0.1.6"
4
4
  end
5
5
  end
@@ -39,22 +39,52 @@ def get_file(file, options={})
39
39
  tempfile.read
40
40
  end
41
41
 
42
- def get_attributes(options={})
42
+ def get_host_attributes(options={})
43
43
  JSON.load(get_file(chef_solo_attributes_file, options))
44
44
  end
45
45
 
46
- def assert_attributes(expected, options={})
47
- found = get_attributes(options)
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
- if found[key] != value
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 assert_run_list(expected, options={})
56
- found = get_attributes(options)["run_list"]
57
- abort("invalid run_list (expected:#{expected.inspect} != found:#{found.inspect})") if found != expected
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
- assert_attributes({"aaa" => "AAA", "bbb" => "BBB", "ccc" => "CCC", "run_list" => %w(recipe[foo] recipe[bar] recipe[baz])})
153
- assert_attributes({"application" => application, "deploy_to" => deploy_to})
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
- assert_run_list(expected)
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
- # assert_run_list(expected) # arguments of chef_solo.run_list will not be written to attributes file
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
- assert_run_list(expected)
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
- # assert_run_list(expected) # arguments of chef_solo.run_list will not be written to attributes file
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
- assert_run_list(expected)
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
- # assert_run_list(expected) # arguments of chef_solo.run_list will not be written to attributes file
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
- assert_run_list(expected)
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
- # assert_run_list(expected) # arguments of chef_solo.run_list will not be written to attributes file
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.5
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-11 00:00:00.000000000 Z
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