conan 0.1.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/README.md +9 -0
- data/bin/conan +10 -0
- data/lib/conan/capistrano.rb +6 -0
- data/lib/conan/deployment.rb +65 -0
- data/lib/conan/deployment/chef.rb +79 -0
- data/lib/conan/deployment/deploy.rb +53 -0
- data/lib/conan/deployment/git.rb +11 -0
- data/lib/conan/initializer.rb +55 -0
- data/lib/conan/smart_hash_merge.rb +34 -0
- data/lib/conan/template/CONAN_TODO +21 -0
- data/lib/conan/template/Capfile +3 -0
- data/lib/conan/template/config/deploy.rb +45 -0
- data/lib/conan/template/config/servers.json +14 -0
- data/lib/conan/template/deploy/chef/bootstrap.sh +52 -0
- data/lib/conan/template/deploy/chef/dna/app.json +6 -0
- data/lib/conan/template/deploy/chef/dna/base.json +37 -0
- data/lib/conan/template/deploy/chef/dna/production.json +15 -0
- data/lib/conan/template/deploy/chef/dna/staging.json +14 -0
- data/lib/conan/template/deploy/chef/recipes/Rakefile +181 -0
- data/lib/conan/template/deploy/chef/recipes/certificates/README +1 -0
- data/lib/conan/template/deploy/chef/recipes/config/client.rb +13 -0
- data/lib/conan/template/deploy/chef/recipes/config/rake.rb +54 -0
- data/lib/conan/template/deploy/chef/recipes/config/server.rb +16 -0
- data/lib/conan/template/deploy/chef/recipes/config/solo.rb +9 -0
- data/lib/conan/template/deploy/chef/recipes/site-cookbooks/README +1 -0
- data/lib/conan/template/deploy/chef/solo.rb +6 -0
- metadata +106 -0
data/README.md
ADDED
data/bin/conan
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Conan
|
2
|
+
class Deployment
|
3
|
+
module Helpers
|
4
|
+
def with_user(new_user, &blk)
|
5
|
+
old_user = user
|
6
|
+
return if old_user == new_user
|
7
|
+
set :user, new_user
|
8
|
+
close_sessions
|
9
|
+
yield
|
10
|
+
set :user, old_user
|
11
|
+
close_sessions
|
12
|
+
end
|
13
|
+
|
14
|
+
def close_sessions
|
15
|
+
sessions.values.each { |session| session.close }
|
16
|
+
sessions.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def rake(command, options={})
|
20
|
+
path = options[:path] || current_path
|
21
|
+
run "cd #{path}; rake #{command} RAILS_ENV=#{rails_env}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def git_tag(source, dest)
|
25
|
+
system "git fetch origin --tags"
|
26
|
+
sha1 = `git rev-parse "#{source}"`
|
27
|
+
system %{git update-ref "refs/tags/#{dest}" #{sha1}}
|
28
|
+
system %{git push -f origin tag #{dest}}
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_role(*roles)
|
32
|
+
roles = Hash.new{ |h,k| h[k] = [] }
|
33
|
+
|
34
|
+
server_config.each do |s, c|
|
35
|
+
c["roles"].each do |r|
|
36
|
+
roles[r.to_sym] << s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
roles.each do |r, ss|
|
41
|
+
next unless roles.include?(r)
|
42
|
+
ss.each_with_index do |s, i|
|
43
|
+
role r, s, :primary => (i == 0)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class <<self
|
50
|
+
def define_tasks(context)
|
51
|
+
load_script(context, "deploy")
|
52
|
+
load_script(context, "chef")
|
53
|
+
load_script(context, "git")
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_script(context, fragment)
|
57
|
+
path = File.expand_path("../deployment/#{fragment}.rb", __FILE__)
|
58
|
+
code = File.read(path)
|
59
|
+
context.instance_eval(code, path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
include Conan::Deployment::Helpers
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
namespace :chef do
|
4
|
+
task :update_aliases do
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
cache_path = "tmp/cache/server_internal_addresses.json"
|
8
|
+
|
9
|
+
cache =
|
10
|
+
if File.exist?(cache_path)
|
11
|
+
JSON.parse(File.read(cache_path))
|
12
|
+
else
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
|
16
|
+
aliases = {}
|
17
|
+
server_config.each do |host, config|
|
18
|
+
cache[host] ||= `ssh #{host} ifconfig`[/inet addr:(10\.\d+\.\d+\.\d+)/, 1]
|
19
|
+
aliases[config["alias"]] = cache[host]
|
20
|
+
end
|
21
|
+
|
22
|
+
FileUtils.mkdir_p File.dirname(cache_path)
|
23
|
+
File.open(cache_path, "w") do |io|
|
24
|
+
io << JSON.dump(cache)
|
25
|
+
end
|
26
|
+
|
27
|
+
File.open("deploy/chef/dna/aliases.json", "w") do |io|
|
28
|
+
io << JSON.dump({"hosts" => aliases})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
before "chef:rsync", "chef:update_aliases"
|
33
|
+
task :rsync do
|
34
|
+
require "json"
|
35
|
+
require "conan/smart_hash_merge"
|
36
|
+
|
37
|
+
ssh_options = {
|
38
|
+
'BatchMode' => 'yes',
|
39
|
+
'CheckHostIP' => 'no',
|
40
|
+
'ForwardAgent' => 'yes',
|
41
|
+
'StrictHostKeyChecking' => 'no',
|
42
|
+
'UserKnownHostsFile' => '/dev/null'
|
43
|
+
}.map{|k, v| "-o #{k}=#{v}"}.join(' ')
|
44
|
+
|
45
|
+
server_config.each do |host, config|
|
46
|
+
dna = {}
|
47
|
+
(["base", "aliases"] + config["roles"]).each do |role|
|
48
|
+
path = "deploy/chef/dna/#{role}.json"
|
49
|
+
next unless File.exist?(path)
|
50
|
+
dna = Conan::SmartHashMerge.merge(dna, JSON.parse(File.read(path)))
|
51
|
+
end
|
52
|
+
|
53
|
+
File.open("deploy/chef/dna/generated.json", "w") do |io|
|
54
|
+
io << JSON.dump(dna)
|
55
|
+
end
|
56
|
+
|
57
|
+
system "rsync -Caz --rsync-path='sudo rsync' --delete --exclude .git --exclude '.*.swp' --rsh='ssh -l ubuntu #{ssh_options}' deploy/chef/ #{host}:/etc/chef"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
before "chef:bootstrap", "chef:rsync"
|
62
|
+
task :bootstrap do
|
63
|
+
with_user "ubuntu" do
|
64
|
+
run "sudo /etc/chef/bootstrap.sh"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
before "chef:update", "chef:bootstrap"
|
69
|
+
task "update" do
|
70
|
+
with_user "ubuntu" do
|
71
|
+
run "sudo chef-solo -j /etc/chef/dna/generated.json"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Install and configure server(s)"
|
77
|
+
task "configure" do
|
78
|
+
chef.update
|
79
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "json"
|
2
|
+
set :server_config, JSON.parse(File.read("config/servers.json"))[stage] || {}
|
3
|
+
|
4
|
+
add_role :app, :db
|
5
|
+
|
6
|
+
namespace :deploy do
|
7
|
+
task :start, :roles => :app do; end
|
8
|
+
task :stop, :roles => :app do; end
|
9
|
+
|
10
|
+
task :restart, :roles => :app do
|
11
|
+
run "touch #{current_path}/tmp/restart.txt"
|
12
|
+
end
|
13
|
+
after "deploy:restart", "deploy:cleanup"
|
14
|
+
|
15
|
+
namespace :maintenance do
|
16
|
+
task :start, :roles => :app do
|
17
|
+
run "cp #{current_path}/public/maintenance.html #{current_path}/public/system/maintenance.html || echo"
|
18
|
+
end
|
19
|
+
|
20
|
+
task :stop, :roles => :app do
|
21
|
+
run "rm -f #{current_path}/public/system/maintenance.html"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
task :link_shared, :roles => :app do
|
26
|
+
shared_paths.each do |s|
|
27
|
+
run "rm -f #{release_path}/#{s}"
|
28
|
+
run "ln -nfs #{shared_path}/#{s} #{release_path}/#{s}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
after "deploy:update_code", "deploy:link_shared"
|
32
|
+
|
33
|
+
desc "Deploy and run migrations"
|
34
|
+
task :default do
|
35
|
+
maintenance.start
|
36
|
+
update_code
|
37
|
+
migrate
|
38
|
+
symlink
|
39
|
+
restart
|
40
|
+
maintenance.stop
|
41
|
+
end
|
42
|
+
|
43
|
+
task :smoke_test do
|
44
|
+
if File.exist?("test/deploy/smoke_test.rb")
|
45
|
+
system "ruby test/deploy/smoke_test.rb #{stage}"
|
46
|
+
unless $? == 0
|
47
|
+
deploy.maintenance.start
|
48
|
+
raise CommandError, "Smoke tests failed"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
after "deploy", "deploy:smoke_test"
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
namespace :git do
|
2
|
+
before "deploy:update_code", "git:tag_attempted_deploy"
|
3
|
+
task :tag_attempted_deploy do
|
4
|
+
git_tag branch, "#{stage}.last-deploy"
|
5
|
+
end
|
6
|
+
|
7
|
+
task :tag_successful_deploy do
|
8
|
+
git_tag branch, "#{stage}.last-successful-deploy"
|
9
|
+
end
|
10
|
+
after "deploy:smoke_test", "git:tag_successful_deploy"
|
11
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Conan
|
4
|
+
class Initializer
|
5
|
+
TEMPLATE_PATH = File.expand_path("../template", __FILE__)
|
6
|
+
ShellCommandError = Class.new(RuntimeError)
|
7
|
+
|
8
|
+
def self.run(where=Dir.pwd)
|
9
|
+
new(where).run
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(where)
|
13
|
+
@destination = File.expand_path(where)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
copy_template
|
18
|
+
add_gitignore
|
19
|
+
add_git_submodule
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def add_gitignore
|
24
|
+
gitignore = ".gitignore"
|
25
|
+
add_newline = File.exist?(gitignore) && File.read(gitignore).match(/[^\n]\Z/)
|
26
|
+
File.open(".gitignore", "a") do |f|
|
27
|
+
f.puts if add_newline
|
28
|
+
f.puts "/deploy/chef/dna/generated.json"
|
29
|
+
f.puts "/deploy/chef/dna/aliases.json"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_git_submodule
|
34
|
+
return unless File.directory?(".git")
|
35
|
+
sh "git submodule add git://github.com/madebymany/cookbooks.git deploy/chef/recipes/cookbooks >/dev/null 2>&1"
|
36
|
+
end
|
37
|
+
|
38
|
+
def copy_template
|
39
|
+
Dir.chdir(TEMPLATE_PATH) do
|
40
|
+
Dir["**/*"].each do |source|
|
41
|
+
target = File.join(@destination, source)
|
42
|
+
if File.directory?(source)
|
43
|
+
FileUtils.mkdir_p target
|
44
|
+
else
|
45
|
+
FileUtils.cp source, target
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def sh(command)
|
52
|
+
system command or raise ShellCommandError, command
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Conan
|
2
|
+
class SmartHashMerge
|
3
|
+
def self.merge(lhash, rhash)
|
4
|
+
new(lhash, rhash).merge
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(lhash, rhash)
|
8
|
+
@lhash, @rhash = lhash, rhash
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge
|
12
|
+
lhash = @lhash.dup
|
13
|
+
deep_merge(lhash, @rhash)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def deep_merge(lhash, rhash)
|
18
|
+
merged = lhash.dup
|
19
|
+
rhash.each do |key, rvalue|
|
20
|
+
lvalue = lhash[key]
|
21
|
+
if lvalue.is_a?(Hash) and rvalue.is_a?(Hash)
|
22
|
+
merged[key] = deep_merge(lhash[key], rhash[key])
|
23
|
+
elsif lvalue.is_a?(Array) and rvalue.is_a?(Array)
|
24
|
+
merged[key] = lvalue + rvalue
|
25
|
+
elsif rvalue.is_a?(Array) && !lvalue.nil?
|
26
|
+
merged[key] = [lvalue] + rvalue
|
27
|
+
else
|
28
|
+
merged[key] = rvalue
|
29
|
+
end
|
30
|
+
end
|
31
|
+
merged
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Conan the Deployer has set up your project.
|
2
|
+
|
3
|
+
Next, set up your deployment:
|
4
|
+
|
5
|
+
* Edit config/servers.json to specify your stages, servers, and roles
|
6
|
+
|
7
|
+
* Edit the TODOs in deploy/chef/dna/*.json
|
8
|
+
|
9
|
+
* Add stage-/role-/server-specific files in deploy/chef/dna/*.json
|
10
|
+
|
11
|
+
* Add some post-deployment tests in test/deploy/smoke_test.rb that test the
|
12
|
+
deployed app.
|
13
|
+
|
14
|
+
* Add your own recipes to deploy/chef/recipes/site-cookbooks
|
15
|
+
|
16
|
+
Finally, use Capistrano to configure your servers and deploy your application
|
17
|
+
to the desired stage:
|
18
|
+
|
19
|
+
* cap staging configure deploy
|
20
|
+
|
21
|
+
This list has been left in CONAN_TODO for you to peruse at your leisure.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Configuration
|
2
|
+
|
3
|
+
# Assume that we are deploying from the "origin" remote.
|
4
|
+
set :repository, `git remote -v | grep 'origin.*fetch' | awk '{print $2}'`.chomp
|
5
|
+
|
6
|
+
# Hopefully, your application name matches the repository name.
|
7
|
+
set :application, File.basename(repository, ".git")
|
8
|
+
|
9
|
+
# Files and directories to persist between deployments:
|
10
|
+
set :shared_paths, %w[
|
11
|
+
config/database.yml
|
12
|
+
log
|
13
|
+
]
|
14
|
+
|
15
|
+
# Your stages. The default assumes:
|
16
|
+
#
|
17
|
+
# * There is a staging and a production environment.
|
18
|
+
# * Staging is deployed from the master branch
|
19
|
+
# * Deployment to production is always made from the last branch successfully
|
20
|
+
# deployed to staging.
|
21
|
+
#
|
22
|
+
|
23
|
+
task :staging do; end
|
24
|
+
task :production do; end
|
25
|
+
|
26
|
+
case ARGV.first.to_sym
|
27
|
+
when :staging
|
28
|
+
set :stage, "staging"
|
29
|
+
set :branch, "master"
|
30
|
+
when :production
|
31
|
+
set :stage, "production"
|
32
|
+
set :branch, "staging.last-successful-deploy"
|
33
|
+
end
|
34
|
+
|
35
|
+
# You probably don't need to edit these
|
36
|
+
default_run_options[:pty] = true
|
37
|
+
set :use_sudo, false
|
38
|
+
set :deploy_via, :remote_cache
|
39
|
+
set :keep_releases, 5
|
40
|
+
set :scm, "git"
|
41
|
+
set :user, "rails"
|
42
|
+
set :deploy_to, "/mnt/#{application}"
|
43
|
+
|
44
|
+
# Let Conan take over
|
45
|
+
require "conan/capistrano"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"staging": {
|
3
|
+
"TODO e.g. something.amazonaws.com": {
|
4
|
+
"roles": ["app", "db", "staging"],
|
5
|
+
"alias": "staging"
|
6
|
+
}
|
7
|
+
},
|
8
|
+
"production": {
|
9
|
+
"TODO e.g. something-else.amazonaws.com": {
|
10
|
+
"roles": ["app", "db", "production"],
|
11
|
+
"alias": "app1"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Bootstrap chef on Ubuntu 10.04
|
3
|
+
|
4
|
+
function inform {
|
5
|
+
status=$1
|
6
|
+
echo
|
7
|
+
echo -e "\e[33m• ${1}\e[0m"
|
8
|
+
echo
|
9
|
+
}
|
10
|
+
|
11
|
+
function happy_ending {
|
12
|
+
echo
|
13
|
+
echo -e "\e[32m✓ ${1}\e[0m"
|
14
|
+
exit 0
|
15
|
+
}
|
16
|
+
|
17
|
+
function croak {
|
18
|
+
echo
|
19
|
+
echo -e "\e[31m✗ $status failed. Aborting.\e[0m"
|
20
|
+
exit 1
|
21
|
+
}
|
22
|
+
|
23
|
+
command -v chef-solo >/dev/null && \
|
24
|
+
happy_ending "Chef is already bootstrapped. Nothing more to do."
|
25
|
+
|
26
|
+
inform "Updating package index"
|
27
|
+
apt-get update || croak
|
28
|
+
|
29
|
+
inform "Setting up build environment"
|
30
|
+
apt-get install -y build-essential || croak
|
31
|
+
|
32
|
+
inform "Installing Ruby Enterprise Edition"
|
33
|
+
case `uname -m` in
|
34
|
+
x86_64)
|
35
|
+
REE="http://rubyforge.org/frs/download.php/71098/ruby-enterprise_1.8.7-2010.02_amd64_ubuntu10.04.deb"
|
36
|
+
;;
|
37
|
+
*)
|
38
|
+
REE="http://rubyforge.org/frs/download.php/71100/ruby-enterprise_1.8.7-2010.02_i386_ubuntu10.04.deb"
|
39
|
+
;;
|
40
|
+
esac
|
41
|
+
echo "Fetching ${REE}"
|
42
|
+
curl -s -L -o ree.deb "${REE}" || croak
|
43
|
+
dpkg -i ree.deb || croak
|
44
|
+
rm ree.deb
|
45
|
+
|
46
|
+
inform "Installing Chef"
|
47
|
+
gem install -v 0.5.8 ohai --no-rdoc --no-ri || croak
|
48
|
+
gem install -v 0.9.12 chef --no-rdoc --no-ri || croak
|
49
|
+
|
50
|
+
mkdir -p /etc/chef
|
51
|
+
|
52
|
+
happy_ending "Chef has been bootstrapped!"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
{
|
2
|
+
"rails_app": {
|
3
|
+
"user": "rails",
|
4
|
+
"name": "TODO",
|
5
|
+
"home": "/mnt/TODO",
|
6
|
+
"packages": [
|
7
|
+
"git-core",
|
8
|
+
"libxml2-dev",
|
9
|
+
"libxslt1-dev"
|
10
|
+
],
|
11
|
+
"canonical_domain": "TODO e.g. www.example.com",
|
12
|
+
"domain_aliases": [
|
13
|
+
"TODO e.g. example.com"
|
14
|
+
],
|
15
|
+
"redirections": [
|
16
|
+
"TODO e.g. ^example.com$"
|
17
|
+
],
|
18
|
+
"database": {
|
19
|
+
"adapter": "mysql2",
|
20
|
+
"encoding": "utf8",
|
21
|
+
"collation": "utf8_general_ci"
|
22
|
+
},
|
23
|
+
"s3": {
|
24
|
+
"access_key_id": "TODO",
|
25
|
+
"secret_access_key": "TODO"
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"apache": {
|
29
|
+
"listen_ports": ["80"]
|
30
|
+
},
|
31
|
+
"passenger": {
|
32
|
+
"version": "2.2.14"
|
33
|
+
},
|
34
|
+
"recipes": [
|
35
|
+
"hosts"
|
36
|
+
]
|
37
|
+
}
|
@@ -0,0 +1,181 @@
|
|
1
|
+
#
|
2
|
+
# Rakefile for Chef Server Repository
|
3
|
+
#
|
4
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2008 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require File.join(File.dirname(__FILE__), 'config', 'rake')
|
22
|
+
|
23
|
+
require 'tempfile'
|
24
|
+
|
25
|
+
if File.directory?(File.join(TOPDIR, ".svn"))
|
26
|
+
$vcs = :svn
|
27
|
+
elsif File.directory?(File.join(TOPDIR, ".git"))
|
28
|
+
$vcs = :git
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Update your repository from source control"
|
32
|
+
task :update do
|
33
|
+
puts "** Updating your repository"
|
34
|
+
|
35
|
+
case $vcs
|
36
|
+
when :svn
|
37
|
+
sh %{svn up}
|
38
|
+
when :git
|
39
|
+
pull = false
|
40
|
+
pull = true if File.join(TOPDIR, ".git", "remotes", "origin")
|
41
|
+
IO.foreach(File.join(TOPDIR, ".git", "config")) do |line|
|
42
|
+
pull = true if line =~ /\[remote "origin"\]/
|
43
|
+
end
|
44
|
+
if pull
|
45
|
+
sh %{git pull}
|
46
|
+
else
|
47
|
+
puts "* Skipping git pull, no origin specified"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
puts "* No SCM configured, skipping update"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Test your cookbooks for syntax errors"
|
55
|
+
task :test do
|
56
|
+
puts "** Testing your cookbooks for syntax errors"
|
57
|
+
|
58
|
+
recipes = ["*cookbooks"].map { |folder|
|
59
|
+
Dir[File.join(TOPDIR, folder, "**", "*.rb")]
|
60
|
+
}.flatten
|
61
|
+
|
62
|
+
recipes.each do |recipe|
|
63
|
+
print "Testing recipe #{recipe}: "
|
64
|
+
sh %{ruby -c #{recipe}} do |ok, res|
|
65
|
+
if ! ok
|
66
|
+
raise "Syntax error in #{recipe}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Install the latest copy of the repository on this Chef Server"
|
73
|
+
task :install => [ :update, :test ] do
|
74
|
+
puts "** Installing your cookbooks"
|
75
|
+
directories = [
|
76
|
+
COOKBOOK_PATH,
|
77
|
+
SITE_COOKBOOK_PATH,
|
78
|
+
CHEF_CONFIG_PATH
|
79
|
+
]
|
80
|
+
puts "* Creating Directories"
|
81
|
+
directories.each do |dir|
|
82
|
+
sh "sudo mkdir -p #{dir}"
|
83
|
+
sh "sudo chown root #{dir}"
|
84
|
+
end
|
85
|
+
puts "* Installing new Cookbooks"
|
86
|
+
sh "sudo rsync -rlP --delete --exclude '.svn' cookbooks/ #{COOKBOOK_PATH}"
|
87
|
+
puts "* Installing new Site Cookbooks"
|
88
|
+
sh "sudo rsync -rlP --delete --exclude '.svn' site-cookbooks/ #{SITE_COOKBOOK_PATH}"
|
89
|
+
# puts "* Installing new Chef Server Config"
|
90
|
+
# sh "sudo cp config/server.rb #{CHEF_SERVER_CONFIG}"
|
91
|
+
# puts "* Installing new Chef Client Config"
|
92
|
+
# sh "sudo cp config/client.rb #{CHEF_CLIENT_CONFIG}"
|
93
|
+
end
|
94
|
+
|
95
|
+
desc "By default, run rake test"
|
96
|
+
task :default => [ :test ]
|
97
|
+
|
98
|
+
desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)"
|
99
|
+
task :new_cookbook do
|
100
|
+
create_cookbook(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_cookbook(dir)
|
104
|
+
raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
|
105
|
+
puts "** Creating cookbook #{ENV["COOKBOOK"]}"
|
106
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "attributes")}"
|
107
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "recipes")}"
|
108
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "definitions")}"
|
109
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "libraries")}"
|
110
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "files", "default")}"
|
111
|
+
sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "templates", "default")}"
|
112
|
+
unless File.exists?(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"))
|
113
|
+
open(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"), "w") do |file|
|
114
|
+
file.puts <<-EOH
|
115
|
+
#
|
116
|
+
# Cookbook Name:: #{ENV["COOKBOOK"]}
|
117
|
+
# Recipe:: default
|
118
|
+
#
|
119
|
+
# Copyright #{Time.now.year}, #{COMPANY_NAME}
|
120
|
+
#
|
121
|
+
EOH
|
122
|
+
case NEW_COOKBOOK_LICENSE
|
123
|
+
when :apachev2
|
124
|
+
file.puts <<-EOH
|
125
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
126
|
+
# you may not use this file except in compliance with the License.
|
127
|
+
# You may obtain a copy of the License at
|
128
|
+
#
|
129
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
130
|
+
#
|
131
|
+
# Unless required by applicable law or agreed to in writing, software
|
132
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
133
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
134
|
+
# See the License for the specific language governing permissions and
|
135
|
+
# limitations under the License.
|
136
|
+
#
|
137
|
+
EOH
|
138
|
+
when :none
|
139
|
+
file.puts <<-EOH
|
140
|
+
# All rights reserved - Do Not Redistribute
|
141
|
+
#
|
142
|
+
EOH
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
desc "Create a new self-signed SSL certificate for FQDN=foo.example.com"
|
149
|
+
task :ssl_cert do
|
150
|
+
$expect_verbose = true
|
151
|
+
fqdn = ENV["FQDN"]
|
152
|
+
fqdn =~ /^(.+?)\.(.+)$/
|
153
|
+
hostname = $1
|
154
|
+
domain = $2
|
155
|
+
raise "Must provide FQDN!" unless fqdn && hostname && domain
|
156
|
+
puts "** Creating self signed SSL Certificate for #{fqdn}"
|
157
|
+
sh("(cd #{CADIR} && openssl genrsa 2048 > #{fqdn}.key)")
|
158
|
+
sh("(cd #{CADIR} && chmod 644 #{fqdn}.key)")
|
159
|
+
puts "* Generating Self Signed Certificate Request"
|
160
|
+
tf = Tempfile.new("#{fqdn}.ssl-conf")
|
161
|
+
ssl_config = <<EOH
|
162
|
+
[ req ]
|
163
|
+
distinguished_name = req_distinguished_name
|
164
|
+
prompt = no
|
165
|
+
|
166
|
+
[ req_distinguished_name ]
|
167
|
+
C = #{SSL_COUNTRY_NAME}
|
168
|
+
ST = #{SSL_STATE_NAME}
|
169
|
+
L = #{SSL_LOCALITY_NAME}
|
170
|
+
O = #{COMPANY_NAME}
|
171
|
+
OU = #{SSL_ORGANIZATIONAL_UNIT_NAME}
|
172
|
+
CN = #{fqdn}
|
173
|
+
emailAddress = #{SSL_EMAIL_ADDRESS}
|
174
|
+
EOH
|
175
|
+
tf.puts(ssl_config)
|
176
|
+
tf.close
|
177
|
+
sh("(cd #{CADIR} && openssl req -config '#{tf.path}' -new -x509 -nodes -sha1 -days 3650 -key #{fqdn}.key > #{fqdn}.crt)")
|
178
|
+
sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{fqdn}.crt > #{fqdn}.info)")
|
179
|
+
sh("(cd #{CADIR} && cat #{fqdn}.crt #{fqdn}.key > #{fqdn}.pem)")
|
180
|
+
sh("(cd #{CADIR} && chmod 644 #{fqdn}.pem)")
|
181
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
This directory contains certificates created by the Rakefile.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#
|
2
|
+
# Chef Client Config File
|
3
|
+
#
|
4
|
+
|
5
|
+
log_level :info
|
6
|
+
log_location STDOUT
|
7
|
+
ssl_verify_mode :verify_none
|
8
|
+
registration_url "http://chef:4000"
|
9
|
+
openid_url "http://chef:4001"
|
10
|
+
template_url "http://chef:4000"
|
11
|
+
remotefile_url "http://chef:4000"
|
12
|
+
search_url "http://chef:4000"
|
13
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
###
|
2
|
+
# Company and SSL Details
|
3
|
+
###
|
4
|
+
|
5
|
+
# The company name - used for SSL certificates, and in srvious other places
|
6
|
+
COMPANY_NAME = "Example Com"
|
7
|
+
|
8
|
+
# The Country Name to use for SSL Certificates
|
9
|
+
SSL_COUNTRY_NAME = "US"
|
10
|
+
|
11
|
+
# The State Name to use for SSL Certificates
|
12
|
+
SSL_STATE_NAME = "Washington"
|
13
|
+
|
14
|
+
# The Locality Name for SSL - typically, the city
|
15
|
+
SSL_LOCALITY_NAME = "Seattle"
|
16
|
+
|
17
|
+
# What department?
|
18
|
+
SSL_ORGANIZATIONAL_UNIT_NAME = "Operations"
|
19
|
+
|
20
|
+
# The SSL contact email address
|
21
|
+
SSL_EMAIL_ADDRESS = "operations@example.com"
|
22
|
+
|
23
|
+
# License for new Cookbooks
|
24
|
+
# Can be :apachev2 or :none
|
25
|
+
NEW_COOKBOOK_LICENSE = :apachev2
|
26
|
+
|
27
|
+
##########################
|
28
|
+
# Chef Repository Layout #
|
29
|
+
##########################
|
30
|
+
|
31
|
+
# Where to find upstream cookbooks
|
32
|
+
COOKBOOK_PATH = "/var/chef/cookbooks"
|
33
|
+
|
34
|
+
# Where to find site-local modifications to upstream cookbooks
|
35
|
+
SITE_COOKBOOK_PATH = "/var/chef/site-cookbooks"
|
36
|
+
|
37
|
+
# Chef Config Path
|
38
|
+
CHEF_CONFIG_PATH = "/etc/chef"
|
39
|
+
|
40
|
+
# The location of the Chef Server Config file (on the server)
|
41
|
+
CHEF_SERVER_CONFIG = File.join(CHEF_CONFIG_PATH, "server.rb")
|
42
|
+
|
43
|
+
# The location of the Chef Client Config file (on the client)
|
44
|
+
CHEF_CLIENT_CONFIG = File.join(CHEF_CONFIG_PATH, "client.rb")
|
45
|
+
|
46
|
+
###
|
47
|
+
# Useful Extras (which you probably don't need to change)
|
48
|
+
###
|
49
|
+
|
50
|
+
# The top of the repository checkout
|
51
|
+
TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
52
|
+
|
53
|
+
# Where to store certificates generated with ssl_cert
|
54
|
+
CADIR = File.expand_path(File.join(TOPDIR, "certificates"))
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# Chef Server Config File
|
3
|
+
#
|
4
|
+
|
5
|
+
log_level :info
|
6
|
+
log_location STDOUT
|
7
|
+
ssl_verify_mode :verify_none
|
8
|
+
registration_url "http://chef:4000"
|
9
|
+
openid_url "http://chef:4001"
|
10
|
+
template_url "http://chef:4000"
|
11
|
+
remotefile_url "http://chef:4000"
|
12
|
+
search_url "http://chef:4000"
|
13
|
+
cookbook_path [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ]
|
14
|
+
merb_log_path "/var/log/chef-server-merb.log"
|
15
|
+
|
16
|
+
Chef::Log::Formatter.show_time = false
|
@@ -0,0 +1 @@
|
|
1
|
+
This directory contains cookbooks that modify upstream ones, or that are specific to your site.
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: conan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Paul Battley
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-09 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: json
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Set up a project to enable the configuration of servers using Chef via Capistrano.
|
36
|
+
email:
|
37
|
+
- pbattley@gmail.com
|
38
|
+
executables:
|
39
|
+
- conan
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- bin/conan
|
46
|
+
- lib/conan/capistrano.rb
|
47
|
+
- lib/conan/smart_hash_merge.rb
|
48
|
+
- lib/conan/deployment/git.rb
|
49
|
+
- lib/conan/deployment/chef.rb
|
50
|
+
- lib/conan/deployment/deploy.rb
|
51
|
+
- lib/conan/deployment.rb
|
52
|
+
- lib/conan/template/config/servers.json
|
53
|
+
- lib/conan/template/config/deploy.rb
|
54
|
+
- lib/conan/template/deploy/chef/dna/base.json
|
55
|
+
- lib/conan/template/deploy/chef/dna/staging.json
|
56
|
+
- lib/conan/template/deploy/chef/dna/app.json
|
57
|
+
- lib/conan/template/deploy/chef/dna/production.json
|
58
|
+
- lib/conan/template/deploy/chef/solo.rb
|
59
|
+
- lib/conan/template/deploy/chef/recipes/config/server.rb
|
60
|
+
- lib/conan/template/deploy/chef/recipes/config/rake.rb
|
61
|
+
- lib/conan/template/deploy/chef/recipes/config/client.rb
|
62
|
+
- lib/conan/template/deploy/chef/recipes/config/solo.rb
|
63
|
+
- lib/conan/template/deploy/chef/recipes/Rakefile
|
64
|
+
- lib/conan/template/deploy/chef/recipes/certificates/README
|
65
|
+
- lib/conan/template/deploy/chef/recipes/site-cookbooks/README
|
66
|
+
- lib/conan/template/deploy/chef/bootstrap.sh
|
67
|
+
- lib/conan/template/CONAN_TODO
|
68
|
+
- lib/conan/template/Capfile
|
69
|
+
- lib/conan/initializer.rb
|
70
|
+
- README.md
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/madebymany/conan
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Conan The Deployer
|
105
|
+
test_files: []
|
106
|
+
|