capistrano-paratrooper-chef 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []