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