capistrano-paratrooper-chef 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 ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capistrano-paratrooper-chef.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Takeshi KOMIYA
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # Paratrooper-chef
2
+
3
+ A capistrano recipe to execute chef-solo in each server.
4
+ All of you can use chef-solo remotely without chef-server.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'capistrano-paratrooper-chef'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install capistrano-paratrooper-chef
19
+
20
+ ## Usage
21
+
22
+ This recipe will execute chef-solo through paratrooper:chef task.
23
+
24
+ To setup paratrooper-chef for your application, add following in you config/deploy.rb.
25
+
26
+ # in "config/deploy.rb"
27
+ require 'capistrano-paratrooper-chef'
28
+
29
+ And then, put your chef-kitchen files to config/ directory.
30
+ by default, paratrooper-chef uses following files and directories.
31
+
32
+ * config/solo.rb
33
+ * config/cookbooks
34
+ * config/site-cookbooks
35
+ * config/roles
36
+ * config/data_bags
37
+
38
+ Finally, run capistrano with paratrooper:chef task. Then chef-solo runs at remote host.
39
+
40
+ $ cap paratrooper:chef
41
+
42
+
43
+ ## Setup chef-solo to remote hosts
44
+
45
+ Paratrooper-chef includes another task to setup chef-solo to remote hosts.
46
+ To enable it, add following in your config/deploy.rb.
47
+
48
+ # in "config/deploy.rb"
49
+ require "capistrano-paratrooper-chef/install"
50
+
51
+ This recipe will install chef-solo during deploy:setup task.
52
+
53
+ ## Define attributes for specific host
54
+
55
+ Paratrooper-chef supports switching attributes for each host.
56
+ Put definition to config/nodes/#{hostname}.json.
57
+
58
+ If there are no defitions for host, paratrooper-chef uses config/solo.rb as attributes.
59
+
60
+ ## Chef roles Auto discovery
61
+
62
+ Chef roles auto discovery appends roles of chef to run_list of each host.
63
+ To enable auto discovery, set :chef_roles_auto_discovery true (as defualt, it is disabled).
64
+
65
+ # in "config/deploy.rb"
66
+ set :chef_roles_auto_discovery, true
67
+
68
+ This feature makes name-based relations with role of capistrano and chef's one::
69
+ * Discovering role definitions of chef from role-names of capistrano that server is assigned
70
+ * Run chef with discovered roles at each server
71
+ * Be able to play different roles of chef for each server
72
+
73
+
74
+ For example, 'web.example.com' plays :web role:
75
+
76
+ set :web, 'web.example.com'
77
+
78
+ And, there is role defition named 'web.json'.
79
+
80
+ # config/roles/web.json
81
+ {
82
+ "nginx" : {
83
+ # ...
84
+ },
85
+ "run_list" : [
86
+ "recipe[nginx]",
87
+ }
88
+ }
89
+
90
+ Then, paratrooper-chef detects automatically these relation, and append 'role[web]' to run_list of web.example.com .
91
+ (do not effect to other hosts)
92
+
93
+ ## Options
94
+
95
+ Following options are available.
96
+
97
+ * Settings for remote host
98
+
99
+ * `:chef_solo_path` - the path of `chef-solo` command. use `chef-solo` by default (search command from $PATH).
100
+ * `:chef_working_dir` - the path where chef-kitchen should installed. use `$HOME/chef-solo` by default.
101
+ * `:chef_cache_dir` - the path for caches. use `/var/chef/cache` by default.
102
+
103
+ * Settings for chef and paratrooper
104
+
105
+ * `:chef_roles_auto_discovery` - Enable "Chef roles Auto discovery". use `false` by default.
106
+ * `:chef_verbose_logging`, - Enable verbose logging mode of `chef-solo`. use `true` by default.
107
+ * `:chef_debug` - Enable debug mode of `chef-solo`. use `false` by default.
108
+
109
+ * Settings for directories
110
+
111
+ * `:chef_kitchen_path` - root directory of kitchen. use `config` by default.
112
+ * `:chef_default_solo_json_path` - default attribute file a.k.a solo.json. use `solo.json` by default.
113
+ * `:chef_cookbooks_path` - cookbooks directories list. use `["cookbooks", "site-cookbooks"]` by default.
114
+ * `:chef_nodes_path` - nodes directory. use `nodes` by default.
115
+ * `:chef_roles_path` - roles directory. use `roles` by default.
116
+ * `:chef_databagas_path` - data bags directory. use `data_bagas` by default.
117
+
118
+ ## Support recipes
119
+
120
+ Following recipes work fine with paratrooper-chef.
121
+
122
+ * rvm-capistrano (https://github.com/wayneeseguin/rvm-capistrano)
123
+ * capistrano-rbenv (https://github.com/yyuu/capistrano-rbenv)
124
+
125
+ ## Contributing
126
+
127
+ 1. Fork it
128
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
129
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
130
+ 4. Push to the branch (`git push origin my-new-feature`)
131
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/capistrano-paratrooper-chef/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Takeshi KOMIYA"]
6
+ gem.email = ["i.tkomiya@gmail.com"]
7
+ gem.description = %q{A capistrano task to invoke chef-solo}
8
+ gem.summary = %q{A capistrano task to invoke chef-solo}
9
+ gem.homepage = "https://github.com/tk0miya/capistrano-paratrooper-chef"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "capistrano-paratrooper-chef"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Capistrano::Paratrooper::Chef::VERSION
17
+
18
+ gem.add_dependency("capistrano")
19
+ end
@@ -0,0 +1,206 @@
1
+ require "json"
2
+ require "tempfile"
3
+ require "capistrano-paratrooper-chef/version"
4
+
5
+
6
+ Capistrano::Configuration.instance.load do
7
+ namespace :paratrooper do
8
+ # directory structure of chef-kitchen
9
+ set :chef_kitchen_path, "config"
10
+ set :chef_default_solo_json_path, "solo.json"
11
+ set :chef_cookbooks_path, ["cookbooks", "site-cookbooks"]
12
+ set :chef_nodes_path, "nodes"
13
+ set :chef_roles_path, "roles"
14
+ set :chef_databags_path, "data_bags"
15
+
16
+ # remote chef settings
17
+ set :chef_solo_path, "chef-solo"
18
+ set :chef_working_dir, "chef-solo"
19
+ set :chef_cache_dir, "/var/chef/cache"
20
+
21
+ # chef settings
22
+ set :chef_roles_auto_discovery, false
23
+ set :chef_verbose_logging, true
24
+ set :chef_debug, false
25
+
26
+ def sudocmd
27
+ envvars = fetch(:default_environment, {}).collect{|k, v| "#{k}=#{v}"}
28
+
29
+ begin
30
+ old_sudo = self[:sudo]
31
+ if fetch(:rvm_type, nil) == :user
32
+ self[:sudo] = "rvmsudo_secure_path=1 #{File.join(rvm_bin_path, "rvmsudo")}"
33
+ end
34
+
35
+ if envvars
36
+ cmd = "#{top.sudo} env #{envvars.join(" ")}"
37
+ else
38
+ cmd = top.sudo
39
+ end
40
+ ensure
41
+ self[:sudo] = old_sudo if old_sudo
42
+ end
43
+
44
+ cmd
45
+ end
46
+
47
+ def sudo(command, *args)
48
+ run "#{sudocmd} #{command}", *args
49
+ end
50
+
51
+ def remote_path(*path)
52
+ File.join(fetch(:chef_working_dir), *path)
53
+ end
54
+
55
+ def cookbooks_paths
56
+ fetch(:chef_cookbooks_path).collect{|path| File.join(fetch(:chef_kitchen_path), path)}
57
+ end
58
+
59
+ def roles_path
60
+ File.join(fetch(:chef_kitchen_path), fetch(:chef_roles_path))
61
+ end
62
+
63
+ def role_exists?(name)
64
+ File.exist?(File.join(roles_path, name.to_s + ".json")) ||
65
+ File.exist?(File.join(roles_path, name.to_s + ".rb"))
66
+ end
67
+
68
+ def databags_path
69
+ File.join(fetch(:chef_kitchen_path), fetch(:chef_databags_path))
70
+ end
71
+
72
+ def nodes_path
73
+ File.join(fetch(:chef_kitchen_path), fetch(:chef_nodes_path))
74
+ end
75
+
76
+
77
+ namespace :run_list do
78
+ def solo_json_path_for(name)
79
+ path = File.join(nodes_path, name.to_s + ".json")
80
+ if File.exist?(path)
81
+ path
82
+ else
83
+ File.join(fetch(:chef_kitchen_path), fetch(:chef_default_solo_json_path))
84
+ end
85
+ end
86
+
87
+ def discover
88
+ find_servers_for_task(current_task).each do |server|
89
+ begin
90
+ open(solo_json_path_for(server.host)) do |fd|
91
+ server.options[:chef_attributes] = JSON.load(fd)
92
+
93
+ if server.options[:chef_attributes]["run_list"].nil?
94
+ server.options[:chef_attributes]["run_list"] = []
95
+ end
96
+ end
97
+ rescue
98
+ server.options[:chef_attributes] = attrs = {"run_list" => []}
99
+ end
100
+
101
+ if fetch(:chef_roles_auto_discovery)
102
+ role_names_for_host(server).each do |role|
103
+ server.options[:chef_attributes]["run_list"] << "role[#{role}]" if role_exists?(role)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def discovered_attributes
110
+ find_servers_for_task(current_task).collect{|server| server.options[:chef_attributes]}.compact
111
+ end
112
+
113
+ def discovered_lists
114
+ discovered_attributes.collect{|attr| attr["run_list"]}
115
+ end
116
+
117
+ def unique?
118
+ if fetch(:chef_roles_auto_discovery)
119
+ discovered_lists.uniq.size == 1
120
+ else
121
+ true
122
+ end
123
+ end
124
+
125
+ def ensure
126
+ if discovered_lists.all?{|run_list| run_list.empty?}
127
+ abort "You must specify at least one recipe or role"
128
+ end
129
+ end
130
+ end
131
+
132
+ namespace :chef do
133
+ task :default, :except => { :no_release => true } do
134
+ run_list.discover
135
+ run_list.ensure
136
+ kitchen.ensure_cookbooks
137
+ kitchen.ensure_working_dir
138
+ kitchen.upload
139
+ chef.generate_solo_rb
140
+ chef.generate_solo_json
141
+ chef.execute
142
+ end
143
+
144
+ task :solo, :except => { :no_release => true } do
145
+ chef.default
146
+ end
147
+
148
+ def generate_solo_rb
149
+ config = <<-CONF
150
+ root = File.expand_path(File.dirname(__FILE__))
151
+ file_cache_path #{fetch(:chef_cache_dir).inspect}
152
+ cookbook_path #{kitchen.cookbooks_paths.inspect}.collect{|dir| File.join(root, dir)}
153
+ role_path File.join(root, #{kitchen.roles_path.inspect})
154
+ data_bag_path File.join(root, #{kitchen.databags_path.inspect})
155
+ verbose_logging #{fetch(:chef_verbose_logging)}
156
+ CONF
157
+ put config, remote_path("solo.rb"), :via => :scp
158
+ end
159
+
160
+ def generate_solo_json
161
+ find_servers_for_task(current_task).each do |server|
162
+ put server.options[:chef_attributes].to_json, remote_path("solo.json"), :hosts => server.host, :via => :scp
163
+ end
164
+ end
165
+
166
+ desc "Run chef-solo"
167
+ task :execute, :except => { :no_release => true } do
168
+ logger.info "Now running chef-solo"
169
+ command = "#{chef_solo_path} -c #{remote_path("solo.rb")} -j #{remote_path("solo.json")}#{' -l debug' if fetch(:chef_debug)}"
170
+ if run_list.unique?
171
+ sudo command
172
+ else
173
+ parallel do |session|
174
+ session.when "options[:chef_attributes]['run_list'].size > 0",
175
+ "#{sudocmd} #{command}"
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ namespace :kitchen do
182
+ def ensure_cookbooks
183
+ abort "No cookbooks found in #{fetch(:cookbooks_directory).inspect}" if kitchen.cookbooks_paths.empty?
184
+ end
185
+
186
+ def ensure_working_dir
187
+ run "rm -rf #{fetch(:chef_working_dir)} && mkdir -p #{fetch(:chef_working_dir)}"
188
+ sudo "mkdir -p #{fetch(:chef_cache_dir)}"
189
+ end
190
+
191
+ desc "Upload files in kitchen"
192
+ task :upload, :except => { :no_release => true } do
193
+ kitchen_paths = [cookbooks_paths, roles_path, databags_path].flatten.compact.select{|d| File.exists?(d)}
194
+ tarball = Tempfile.new("kitchen.tar")
195
+ begin
196
+ tarball.close
197
+ system "tar -czf #{tarball.path} #{kitchen_paths.join(' ')}"
198
+ top.upload tarball.path, remote_path("kitchen.tar"), :via => :scp
199
+ run "cd #{fetch(:chef_working_dir)} && tar -xzf kitchen.tar"
200
+ ensure
201
+ tarball.unlink
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,41 @@
1
+ require 'capistrano-paratrooper-chef'
2
+
3
+ Capistrano::Configuration.instance.load do
4
+ namespace :paratrooper do
5
+ namespace :chef do
6
+ set :chef_version, ">= 11.0.0"
7
+
8
+ on :load do
9
+ if top.namespaces.key?(:rbenv)
10
+ after "rbenv:setup", "paratrooper:chef:setup"
11
+ elsif top.namespaces.key?(:rvm)
12
+ after "rvm:install_ruby", "paratrooper:chef:setup"
13
+ else
14
+ after "deploy:setup" "paratrooper:chef:setup"
15
+ end
16
+ end
17
+
18
+ desc "Installs chef"
19
+ task :setup, :except => { :no_release => true } do
20
+ required_version = fetch(:chef_version).inspect
21
+ installed = capture("gem list -i chef -v #{required_version} || true").strip
22
+
23
+ if installed == "false"
24
+ if fetch(:rvm_type, nil) == :user or fetch(:rbenv_path, nil)
25
+ run "gem uninstall -xaI chef || true"
26
+ run "gem install chef -v #{fetch(:chef_version).inspect} --quiet --no-ri --no-rdoc"
27
+ run "gem install ruby-shadow --quiet --no-ri --no-rdoc"
28
+
29
+ if fetch(:rbenv_path, nil)
30
+ rbenv.rehash
31
+ end
32
+ else
33
+ sudo "gem uninstall -xaI chef || true"
34
+ sudo "gem install chef -v #{fetch(:chef_version).inspect} --quiet --no-ri --no-rdoc"
35
+ sudo "gem install ruby-shadow --quiet --no-ri --no-rdoc"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ module Capistrano
2
+ module Paratrooper
3
+ module Chef
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-paratrooper-chef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Takeshi KOMIYA
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: capistrano
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: A capistrano task to invoke chef-solo
31
+ email:
32
+ - i.tkomiya@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - capistrano-paratrooper-chef.gemspec
43
+ - lib/capistrano-paratrooper-chef.rb
44
+ - lib/capistrano-paratrooper-chef/install.rb
45
+ - lib/capistrano-paratrooper-chef/version.rb
46
+ homepage: https://github.com/tk0miya/capistrano-paratrooper-chef
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.24
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A capistrano task to invoke chef-solo
70
+ test_files: []