chef_cap 0.0.5
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 +2 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +19 -0
- data/README.rdoc +49 -0
- data/Rakefile +10 -0
- data/chef_cap.gemspec +21 -0
- data/fixtures/parser.json +3 -0
- data/fixtures/ssh_deploy_key +1 -0
- data/fixtures/ssh_public_key +1 -0
- data/init.rb +1 -0
- data/lib/chef_cap/tasks.rb +18 -0
- data/lib/chef_cap/version.rb +3 -0
- data/lib/chef_cap.rb +14 -0
- data/lib/generators/chef_cap/install_generator.rb +14 -0
- data/lib/generators/chef_cap/templates/Capfile +18 -0
- data/lib/generators/chef_cap/templates/chef/cookbooks/gems/recipes/default.rb +11 -0
- data/lib/generators/chef_cap/templates/chef/node.json +50 -0
- data/recipes/chef_cap.rb +196 -0
- data/recipes/cook.rb +7 -0
- data/recipes/lib/chef_cap_configuration.rb +19 -0
- data/recipes/lib/chef_cap_helper.rb +77 -0
- data/recipes/lib/chef_cap_initializer.rb +6 -0
- data/recipes/lib/chef_dna_parser.rb +36 -0
- data/recipes/rvm_bootstrap.rb +34 -0
- data/recipes/unset_default_deploy.rb +44 -0
- data/spec/chef_cap_configuration_spec.rb +27 -0
- data/spec/chef_cap_helper_spec.rb +126 -0
- data/spec/chef_cap_mock_cap.rb +267 -0
- data/spec/chef_cap_spec.rb +808 -0
- data/spec/chef_dna_parser_spec.rb +29 -0
- data/spec/spec_helper.rb +45 -0
- metadata +117 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2-p0@chef_cap
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
capistrano (2.5.19)
|
5
|
+
highline
|
6
|
+
net-scp (>= 1.0.0)
|
7
|
+
net-sftp (>= 2.0.0)
|
8
|
+
net-ssh (>= 2.0.14)
|
9
|
+
net-ssh-gateway (>= 1.0.0)
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
highline (1.6.1)
|
12
|
+
net-scp (1.0.4)
|
13
|
+
net-ssh (>= 1.99.1)
|
14
|
+
net-sftp (2.0.5)
|
15
|
+
net-ssh (>= 2.0.9)
|
16
|
+
net-ssh (2.0.23)
|
17
|
+
net-ssh-gateway (1.0.1)
|
18
|
+
net-ssh (>= 1.99.1)
|
19
|
+
rspec (2.1.0)
|
20
|
+
rspec-core (~> 2.1.0)
|
21
|
+
rspec-expectations (~> 2.1.0)
|
22
|
+
rspec-mocks (~> 2.1.0)
|
23
|
+
rspec-core (2.1.0)
|
24
|
+
rspec-expectations (2.1.0)
|
25
|
+
diff-lcs (~> 1.1.2)
|
26
|
+
rspec-mocks (2.1.0)
|
27
|
+
rspec-rails (2.1.0)
|
28
|
+
rspec (~> 2.1.0)
|
29
|
+
wirble (0.1.3)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
capistrano
|
36
|
+
rspec-rails (= 2.1)
|
37
|
+
wirble
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010-11 Case Commons, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= Chef Cap
|
2
|
+
|
3
|
+
* http://github.com/casecommons/chef_cap/
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
capistrano + chef-solo == deployment + server automation
|
8
|
+
|
9
|
+
Using chef's JSON configuration format to drive capistrano and chef-solo so you can use both to not only deploy your application but also completely automate the configuration of your servers.
|
10
|
+
|
11
|
+
== INSTALL
|
12
|
+
|
13
|
+
Add chef_cap to your Gemfile
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
gem 'chef_cap'
|
17
|
+
end
|
18
|
+
|
19
|
+
Then run:
|
20
|
+
|
21
|
+
$ bundle install
|
22
|
+
|
23
|
+
=== Rails 3
|
24
|
+
|
25
|
+
$ rails generate chef_cap:install
|
26
|
+
|
27
|
+
=== Rails 2
|
28
|
+
|
29
|
+
Edit your Rakefile and add:
|
30
|
+
|
31
|
+
require 'chef_cap/tasks'
|
32
|
+
|
33
|
+
Then run:
|
34
|
+
|
35
|
+
$ bundle exec rake chef_cap:install
|
36
|
+
|
37
|
+
Which will overwrite your Capfile and create a chef directory with sample files unless one already exists.
|
38
|
+
|
39
|
+
== REQUIREMENTS
|
40
|
+
|
41
|
+
* Capistrano 2.x
|
42
|
+
|
43
|
+
== LICENSE
|
44
|
+
|
45
|
+
MIT
|
46
|
+
|
47
|
+
== NOTICE
|
48
|
+
|
49
|
+
Chef and chef-solo are © 2010 Opscode (http://www.opscode.com/)
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
require 'rake'
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
data/chef_cap.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "chef_cap/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "chef_cap"
|
7
|
+
s.version = ChefCap::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Case Commons, LLC"]
|
10
|
+
s.email = ["casecommons-dev@googlegroups.com"]
|
11
|
+
s.homepage = "https://github.com/Casecommons/chef_cap"
|
12
|
+
s.license = "MIT"
|
13
|
+
s.add_dependency('capistrano', '>= 2.5.5')
|
14
|
+
s.summary = %q{capistrano + chef-solo == chef_cap"}
|
15
|
+
s.description = %q{chef_cap uses chef"s JSON config format to drive both capistrano and chef-solo"}
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
dsa imaprivatekey
|
@@ -0,0 +1 @@
|
|
1
|
+
imapublickey
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "chef_cap"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
namespace :chef_cap do
|
4
|
+
desc "Install chefcap JSON and cookbooks for the first time"
|
5
|
+
task :install => :environment do
|
6
|
+
require "fileutils"
|
7
|
+
templates_path = File.join(File.dirname(__FILE__), "..", "generators", "chef_cap", "templates", "chef")
|
8
|
+
|
9
|
+
FileUtils.cp_r File.join(templates_path, "..", "Capfile"), File.join(Rails.root, "Capfile")
|
10
|
+
|
11
|
+
if File.exist?(Rails.root + "chef/node.json") || File.directory?(Rails.root + "chef/cookbooks")
|
12
|
+
puts "Already initialized chef_cap?"
|
13
|
+
else
|
14
|
+
FileUtils.mkdir_p(Rails.root + "chef")
|
15
|
+
FileUtils.cp_r Dir.glob(templates_path, "/*"), (Rails.root + "chef")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/chef_cap.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "chef_cap/version"
|
2
|
+
|
3
|
+
module ChefCap
|
4
|
+
|
5
|
+
class Capistrano
|
6
|
+
RECIPES_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "recipes"))
|
7
|
+
|
8
|
+
def self.load_recipes(capistrano)
|
9
|
+
Dir[File.join(RECIPES_PATH, "*.rb")].each { |recipe| capistrano.send(:load, recipe) }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ChefCap
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
def install
|
7
|
+
copy_file "Capfile", "Capfile"
|
8
|
+
directory "chef/cookbooks/gems/recipes"
|
9
|
+
copy_file "chef/node.json", "chef/node.json"
|
10
|
+
copy_file "chef/cookbooks/gems/recipes/default.rb", "chef/cookbooks/gems/recipes/default.rb"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Set up gems listed in the Gemfile.
|
4
|
+
gemfile = File.expand_path('Gemfile', __FILE__)
|
5
|
+
begin
|
6
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
7
|
+
require 'bundler'
|
8
|
+
Bundler.setup
|
9
|
+
rescue Bundler::GemNotFound => e
|
10
|
+
STDERR.puts e.message
|
11
|
+
STDERR.puts "Try running `bundle install`."
|
12
|
+
exit!
|
13
|
+
end if File.exist?(gemfile)
|
14
|
+
require "chef_cap"
|
15
|
+
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
16
|
+
::ChefCap::Capistrano.load_recipes(self)
|
17
|
+
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
|
18
|
+
load 'config/deploy'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
{
|
2
|
+
"application": {
|
3
|
+
"name": "your-app-name",
|
4
|
+
"repository": "your-repo"
|
5
|
+
},
|
6
|
+
"upload": [
|
7
|
+
],
|
8
|
+
"environments": {
|
9
|
+
"defaults": {
|
10
|
+
"user": "deploy",
|
11
|
+
"branch": "master",
|
12
|
+
"use_sudo": false,
|
13
|
+
"ssh": {
|
14
|
+
"deploy_key_file": "<%= ENV['HOME'] %>/.ssh/servers_deploy_key",
|
15
|
+
"authorized_pub_file": "<%= ENV['HOME'] %>/.ssh/your_workstations_public_key.pub",
|
16
|
+
"known_hosts": "github.com,207.97.227.239 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==",
|
17
|
+
"options": {
|
18
|
+
"keys": "<%= ENV['HOME'] %>/.ssh/your_workstations_private_key"
|
19
|
+
}
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"production": {
|
23
|
+
"rails_env": "production",
|
24
|
+
"database": {
|
25
|
+
"user": "production-db-user",
|
26
|
+
"host": "production-db-host"
|
27
|
+
},
|
28
|
+
"servers": [
|
29
|
+
{
|
30
|
+
"hostname": "some.fq.dn",
|
31
|
+
"roles": ["web", "app", "db"],
|
32
|
+
"primary": ["web", "app", "db"]
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
},
|
37
|
+
"shared": {
|
38
|
+
"gems": [
|
39
|
+
{ "name": "bundler" },
|
40
|
+
{ "name": "chef", "version": ">=0.9.12" }
|
41
|
+
],
|
42
|
+
"run_list": ["gems"]
|
43
|
+
},
|
44
|
+
"roles": {
|
45
|
+
"web": {},
|
46
|
+
"app": {},
|
47
|
+
"db": {}
|
48
|
+
}
|
49
|
+
// "deploy_recipe": "yourcustomdeployrecipe",
|
50
|
+
}
|
data/recipes/chef_cap.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "lib", "chef_dna_parser"))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "lib", "chef_cap_helper"))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "lib", "chef_cap_configuration"))
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "lib", "chef_cap_initializer"))
|
5
|
+
|
6
|
+
class DnaConfigurationError < Exception; end
|
7
|
+
|
8
|
+
ChefCapConfiguration.configuration = self
|
9
|
+
ChefDnaParser.load_dna
|
10
|
+
|
11
|
+
before "deploy", "chef:setup"
|
12
|
+
|
13
|
+
set :application, ChefDnaParser.parsed["application"]["name"] rescue nil
|
14
|
+
set :repository, ChefDnaParser.parsed["application"]["repository"] rescue nil
|
15
|
+
|
16
|
+
ChefCapConfiguration.set_repository_settings
|
17
|
+
|
18
|
+
if ChefDnaParser.parsed["environments"]
|
19
|
+
if environment_defaults = ChefDnaParser.parsed["environments"]["defaults"]
|
20
|
+
ChefCapHelper.parse_hash(environment_defaults)
|
21
|
+
end
|
22
|
+
|
23
|
+
ChefDnaParser.parsed["environments"].each_key do |environment|
|
24
|
+
next if environment == "default"
|
25
|
+
environment_hash = ChefDnaParser.parsed["environments"][environment]
|
26
|
+
|
27
|
+
desc "Set server roles for the #{environment} environment"
|
28
|
+
task environment.to_sym do
|
29
|
+
set :environment_settings, environment_hash
|
30
|
+
set :rails_env, environment_hash["rails_env"] || environment
|
31
|
+
default_environment["RAILS_ENV"] = rails_env
|
32
|
+
|
33
|
+
ChefCapHelper.parse_hash(environment_hash)
|
34
|
+
|
35
|
+
(environment_hash["servers"] || []).each do |server|
|
36
|
+
if server["roles"] && server["hostname"]
|
37
|
+
server["roles"].each do |role|
|
38
|
+
options = {}
|
39
|
+
options[:primary] = true if server["primary"] && server["primary"].include?(role)
|
40
|
+
role role.to_sym, server["hostname"], options
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private_key = ssh_deploy_key_file rescue false
|
49
|
+
public_key = ssh_authorized_pub_file rescue false
|
50
|
+
known_hosts = ssh_known_hosts rescue false
|
51
|
+
if private_key || public_key || known_hosts
|
52
|
+
private_key_remote_file = ".ssh/id_rsa"
|
53
|
+
if private_key
|
54
|
+
key_contents = File.read(private_key)
|
55
|
+
private_key_remote_file = ".ssh/id_dsa" if key_contents =~ /DSA/i
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
namespace :ssh do
|
60
|
+
desc "Transfer SSH keys to the remote server"
|
61
|
+
task :transfer_keys do
|
62
|
+
run "mkdir -p ~/.ssh"
|
63
|
+
if private_key
|
64
|
+
put(File.read(private_key), private_key_remote_file, :mode => "0600")
|
65
|
+
end
|
66
|
+
put(File.read(public_key), ".ssh/authorized_keys", :mode => "0600") if public_key
|
67
|
+
put(known_hosts, ".ssh/known_hosts", :mode => "0600") if known_hosts
|
68
|
+
end
|
69
|
+
end
|
70
|
+
before "chef:setup", "ssh:transfer_keys"
|
71
|
+
|
72
|
+
depend(:remote, :file, private_key_remote_file) if private_key
|
73
|
+
depend(:remote, :file, ".ssh/authorized_keys") if public_key
|
74
|
+
depend(:remote, :file, ".ssh/known_hosts") if known_hosts
|
75
|
+
end
|
76
|
+
|
77
|
+
ssh_options[:paranoid] = ssh_options_paranoid rescue nil
|
78
|
+
ssh_options[:keys] = ssh_options_keys rescue nil
|
79
|
+
ssh_options[:forward_agent] = ssh_options_forward_agent rescue nil
|
80
|
+
ssh_options[:username] = ssh_options_username rescue user rescue nil
|
81
|
+
|
82
|
+
if ChefDnaParser.parsed["upload"]
|
83
|
+
uploads_for_roles = {}
|
84
|
+
ChefDnaParser.parsed["upload"].each do |upload|
|
85
|
+
unless upload.has_key?("source") && upload.has_key?("destination") && upload.has_key?("roles")
|
86
|
+
raise DnaConfigurationError, "Invalid upload entry, should be {'source':value, 'destination':value, 'roles':[list], 'mode':value}"
|
87
|
+
end
|
88
|
+
upload["roles"].each do |role|
|
89
|
+
uploads_for_roles[role] ||= []
|
90
|
+
uploads_for_roles[role] << [upload["source"], upload["destination"], {:mode => upload["mode"] || "0644"}]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
uploads_for_roles.each_pair do |role, file_uploads|
|
95
|
+
task "chef_upload_for_#{role}".to_sym, :roles => role do
|
96
|
+
file_uploads.each do |file_to_upload|
|
97
|
+
# TODO: better then mac local -> linux remote compatibility here
|
98
|
+
run "md5sum #{file_to_upload[1]} | cut -f1 -d ' '" do |channel, stream, data|
|
99
|
+
remote_md5 = data.to_s.strip
|
100
|
+
local_md5 = `md5 #{file_to_upload[0]} | cut -f 2 -d '='`.to_s.strip
|
101
|
+
if remote_md5 == local_md5
|
102
|
+
puts "#{File.basename(file_to_upload[1])} matches checksum, skipping"
|
103
|
+
else
|
104
|
+
upload file_to_upload[0], file_to_upload[1], :host => channel[:host]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
namespace :chef do
|
112
|
+
desc "Uploads specified files to remote server"
|
113
|
+
task :upload_all do
|
114
|
+
uploads_for_roles.keys.each do |role|
|
115
|
+
send "chef_upload_for_#{role}".to_sym
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
before "chef:deploy", "chef:upload_all"
|
121
|
+
end
|
122
|
+
|
123
|
+
if ChefDnaParser.parsed["chef"] && ChefDnaParser.parsed["chef"]["root"]
|
124
|
+
set :chef_root_path, ChefDnaParser.parsed["chef"]["root"]
|
125
|
+
elsif ChefDnaParser.file_path
|
126
|
+
default_chef_path = File.expand_path(File.join(File.dirname(ChefDnaParser.file_path)))
|
127
|
+
if File.directory?(File.join(default_chef_path, "cookbooks"))
|
128
|
+
set :chef_root_path, default_chef_path
|
129
|
+
end
|
130
|
+
else
|
131
|
+
raise DnaConfigurationError, "Could not find cookbooks in JSON or as a subdirectory of where your JSON is!"
|
132
|
+
end
|
133
|
+
|
134
|
+
namespace :chef do
|
135
|
+
desc "Setup chef solo on the server(s)"
|
136
|
+
task :setup do
|
137
|
+
sudo "rvm default exec gem specification --version '>=0.9.12' chef 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }' || sudo rvm default exec gem install chef --no-ri --no-rdoc && echo 'Chef Solo already on this server.'"
|
138
|
+
sudo "rvm default exec which chef-solo"
|
139
|
+
end
|
140
|
+
|
141
|
+
desc "Run chef-solo on the server(s)"
|
142
|
+
task :deploy do
|
143
|
+
put "cookbook_path '/tmp/chef-cap-#{rails_env}/cookbooks'", "/tmp/chef-cap-solo-#{rails_env}.rb", :mode => "0600"
|
144
|
+
sudo "rm -rf /tmp/chef-cap-#{rails_env}"
|
145
|
+
upload chef_root_path, "/tmp/chef-cap-#{rails_env}", :mode => "0700"
|
146
|
+
|
147
|
+
|
148
|
+
begin
|
149
|
+
env_settings = environment_settings
|
150
|
+
rescue
|
151
|
+
raise "Could not load environment_settings, usually this means you tried to run the deploy task without calling an <env> first"
|
152
|
+
end
|
153
|
+
parallel do |session|
|
154
|
+
session.else "echo 'Deploying Chef to this machine'" do |channel, stream, data|
|
155
|
+
roles_for_host = ChefCapHelper.roles_for_host(roles, channel[:host])
|
156
|
+
|
157
|
+
json_to_modify = ChefDnaParser.parsed.dup
|
158
|
+
hash_for_host = ChefCapHelper.merge_roles_for_host(json_to_modify["roles"], roles_for_host)
|
159
|
+
|
160
|
+
shared_hash = json_to_modify["shared"] || {}
|
161
|
+
shared_hash.each { |k, v| ChefCapHelper.recursive_merge(json_to_modify, k, v) }
|
162
|
+
hash_for_host.each {|k, v| ChefCapHelper.recursive_merge(json_to_modify, k, v) }
|
163
|
+
|
164
|
+
json_to_modify["environment"] ||= json_to_modify["environments"]["defaults"] || {} rescue {}
|
165
|
+
env_settings.each { |k, v| ChefCapHelper.recursive_merge(json_to_modify["environment"] || {}, k, v) }
|
166
|
+
|
167
|
+
json_to_modify["environment"]["revision"] = ChefCapHelper.set_revision if ChefCapHelper.has_revision?
|
168
|
+
json_to_modify["environment"]["servers"] = ChefCapHelper.intialize_primary_values(json_to_modify["environment"]["servers"])
|
169
|
+
|
170
|
+
should_not_deploy = no_deploy rescue false
|
171
|
+
json_to_modify["run_list"] = ChefCapHelper.rewrite_run_list_for_deploy(json_to_modify, should_not_deploy)
|
172
|
+
|
173
|
+
set "node_hash_for_#{channel[:host].gsub(/\./, "_")}", json_to_modify
|
174
|
+
put json_to_modify.to_json, "/tmp/chef-cap-#{rails_env}-#{channel[:host]}.json", :mode => "0600"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
chef.run_chef_solo
|
179
|
+
end
|
180
|
+
|
181
|
+
task :run_chef_solo do
|
182
|
+
db = find_servers(:roles => [:db]).map(&:host)
|
183
|
+
app_and_web_only = find_servers(:roles => [:app, :web]).map(&:host) - db
|
184
|
+
|
185
|
+
run_chef_solo = "env PATH=$PATH:/usr/sbin rvm default exec chef-solo -c /tmp/chef-cap-solo-#{rails_env}.rb -j /tmp/chef-cap-#{rails_env}-`hostname`.json #{ENV['DEBUG'] ? '-l debug' : ''}"
|
186
|
+
sudo(run_chef_solo, :hosts => db) if db.any?
|
187
|
+
sudo(run_chef_solo, :hosts => app_and_web_only) if app_and_web_only.any?
|
188
|
+
end
|
189
|
+
|
190
|
+
desc "Remove all chef-cap files from /tmp"
|
191
|
+
task :cleanup do
|
192
|
+
sudo "rm -rf /tmp/chef-cap*"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
before "chef:deploy", "chef:setup"
|
data/recipes/cook.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class ChefCapConfiguration
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
|
5
|
+
def set_repository_settings
|
6
|
+
repository_value = @configuration.send(:repository) rescue false
|
7
|
+
case repository_value
|
8
|
+
when /git/
|
9
|
+
@configuration.send(:set, :scm, :git)
|
10
|
+
@configuration.send(:set, :git_enable_submodules, 1)
|
11
|
+
@configuration.send(:default_run_options)[:pty] = true
|
12
|
+
@configuration.send(:depend, :remote, :command, "git")
|
13
|
+
when /svn/
|
14
|
+
@configuration.send(:set, :scm, :svn)
|
15
|
+
@configuration.send(:depend, :remote, :command, "svn")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class ChefCapHelper
|
2
|
+
class << self
|
3
|
+
def debug(message)
|
4
|
+
puts "** #{message}" if ENV["DEBUG"]
|
5
|
+
end
|
6
|
+
|
7
|
+
def parse_hash(hash, prefix = nil)
|
8
|
+
hash.each do |key, value|
|
9
|
+
if value.is_a? Hash
|
10
|
+
parse_hash(value, [prefix, key].compact.join("_"))
|
11
|
+
else
|
12
|
+
key = [prefix, key].compact.join("_").to_sym
|
13
|
+
debug("Setting #{key.inspect} => #{value.inspect}")
|
14
|
+
ChefCapConfiguration.configuration.set key, value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def recursive_merge(hash, key, value)
|
20
|
+
case "#{hash[key].class}_#{value.class}"
|
21
|
+
when "Array_Array"
|
22
|
+
hash[key] = hash[key] | value
|
23
|
+
when "Hash_Hash"
|
24
|
+
hash[key] = hash[key].merge(value)
|
25
|
+
else
|
26
|
+
hash[key] = value
|
27
|
+
end
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def roles_for_host(roles, current_host)
|
32
|
+
roles.select do |role_name, role|
|
33
|
+
role.servers.map(&:host).include?(current_host)
|
34
|
+
end.map(&:first).map(&:to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
def merge_roles_for_host(roles_hash, roles_for_host)
|
38
|
+
return {} if roles_hash.nil?
|
39
|
+
|
40
|
+
merged_hash_for_host = {}
|
41
|
+
roles_hash.each do |role_name, role_hash|
|
42
|
+
if roles_for_host.include?(role_name.to_s)
|
43
|
+
role_hash.each do |role_hash_key, role_hash_key_value|
|
44
|
+
merged_hash_for_host.merge(recursive_merge(merged_hash_for_host, role_hash_key, role_hash_key_value))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
merged_hash_for_host
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_revision
|
52
|
+
ENV["rev"] || ENV["tag"] || ENV["REV"] || ENV["TAG"] || ENV["revision"] || ENV["REVISION"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_revision?
|
56
|
+
!set_revision.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def rewrite_run_list_for_deploy(json_hash, should_not_deploy = false)
|
60
|
+
return nil if json_hash["run_list"].nil?
|
61
|
+
new_run_list = json_hash["run_list"].dup
|
62
|
+
if json_hash.has_key? "deploy_recipe"
|
63
|
+
deploy_recipe = new_run_list.delete(json_hash["deploy_recipe"])
|
64
|
+
new_run_list << deploy_recipe if deploy_recipe && !should_not_deploy
|
65
|
+
end
|
66
|
+
new_run_list
|
67
|
+
end
|
68
|
+
|
69
|
+
def intialize_primary_values(array_of_server_hashes)
|
70
|
+
array_of_server_hashes.each do |server_hash|
|
71
|
+
server_hash["primary"] ||= []
|
72
|
+
end
|
73
|
+
array_of_server_hashes
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "json"
|
2
|
+
require "erb"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
class ChefDnaParser
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :dna, :parsed, :file_path, :test_dna
|
9
|
+
|
10
|
+
def test_dna=(json)
|
11
|
+
@test_dna = json
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_dna
|
15
|
+
@parsed ||= {}
|
16
|
+
@dna ||= ""
|
17
|
+
if @test_dna
|
18
|
+
@dna = ERB.new(@test_dna).result(binding)
|
19
|
+
else
|
20
|
+
@file_path = ENV["DNA"] || ENV["dna"] || default_dna_path
|
21
|
+
|
22
|
+
@dna = ERB.new(File.read(file_path)).result(binding)
|
23
|
+
end
|
24
|
+
@parsed = JSON.parse(@dna)
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_dna_path
|
28
|
+
File.join(rails_root, "chef", "node.json")
|
29
|
+
end
|
30
|
+
|
31
|
+
def rails_root
|
32
|
+
FileUtils.pwd
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|