deploify 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +36 -0
- data/LICENSE.txt +22 -0
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/deploify.gemspec +29 -0
- data/lib/deploify/capistrano_extensions.rb +189 -0
- data/lib/deploify/recipes/deploify.rb +171 -0
- data/lib/deploify/recipes/mongodb.rb +15 -0
- data/lib/deploify/recipes/monit.rb +84 -0
- data/lib/deploify/recipes/mysql.rb +85 -0
- data/lib/deploify/recipes/nginx.rb +122 -0
- data/lib/deploify/recipes/passenger.rb +130 -0
- data/lib/deploify/recipes/puma.rb +15 -0
- data/lib/deploify/recipes/rails.rb +221 -0
- data/lib/deploify/recipes/thin.rb +104 -0
- data/lib/deploify/recipes.rb +13 -0
- data/lib/deploify/templates/monit/monit.conf.erb +5 -0
- data/lib/deploify/templates/nginx/logrotate.conf.erb +12 -0
- data/lib/deploify/templates/nginx/vhost_http_force_ssl.conf.erb +79 -0
- data/lib/deploify/templates/nginx/vhost_http_only.conf.erb +66 -0
- data/lib/deploify/templates/nginx/vhost_http_with_ssl.conf.erb +131 -0
- data/lib/deploify/templates/passenger/logrotate.conf.erb +12 -0
- data/lib/deploify/templates/passenger/passengerctl-lib.erb +68 -0
- data/lib/deploify/templates/passenger/passengerctl.erb +10 -0
- data/lib/deploify/templates/thin/logrotate.conf.erb +0 -0
- data/lib/deploify/templates/thin/thinctl-lib.erb +84 -0
- data/lib/deploify/templates/thin/thinctl.erb +9 -0
- data/lib/deploify/version.rb +12 -0
- data/lib/deploify.rb +7 -0
- data/lib/plugins/all.rb +20 -0
- data/lib/plugins/apt.rb +94 -0
- data/lib/plugins/std.rb +203 -0
- metadata +196 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
deploify (0.2.0)
|
5
|
+
bundler (>= 1.0.21)
|
6
|
+
capistrano (~> 2.13.5)
|
7
|
+
rvm-capistrano (~> 1.2.7)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
capistrano (2.13.5)
|
13
|
+
highline
|
14
|
+
net-scp (>= 1.0.0)
|
15
|
+
net-sftp (>= 2.0.0)
|
16
|
+
net-ssh (>= 2.0.14)
|
17
|
+
net-ssh-gateway (>= 1.1.0)
|
18
|
+
highline (1.6.15)
|
19
|
+
net-scp (1.0.4)
|
20
|
+
net-ssh (>= 1.99.1)
|
21
|
+
net-sftp (2.0.5)
|
22
|
+
net-ssh (>= 2.0.9)
|
23
|
+
net-ssh (2.6.2)
|
24
|
+
net-ssh-gateway (1.1.0)
|
25
|
+
net-ssh (>= 1.99.1)
|
26
|
+
rvm-capistrano (1.2.7)
|
27
|
+
capistrano (>= 2.0.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
bundler (>= 1.0.21)
|
34
|
+
capistrano (~> 2.13.5)
|
35
|
+
deploify!
|
36
|
+
rvm-capistrano (~> 1.2.7)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Richard Říman
|
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
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/deploify.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'deploify/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "deploify"
|
8
|
+
gem.version = Deploify::VERSION
|
9
|
+
gem.authors = ["Richard R\314\214i\314\201man"]
|
10
|
+
gem.email = ["riman.richard@gmail.com"]
|
11
|
+
gem.description = "deploify - capistrano based and deprec inspired deploy solution served as a gem"
|
12
|
+
gem.summary = "capistrano based and deprec inspired deploy gem"
|
13
|
+
gem.homepage = "https://github.com/richardriman/deploify"
|
14
|
+
|
15
|
+
gem.required_ruby_version = ">= 1.8.7"
|
16
|
+
|
17
|
+
gem.add_development_dependency "bundler", ">= 1.0.21"
|
18
|
+
gem.add_development_dependency "capistrano", "~> 2.13.5"
|
19
|
+
gem.add_development_dependency "rvm-capistrano", "~> 1.2.7"
|
20
|
+
|
21
|
+
gem.add_dependency "bundler", ">= 1.0.21"
|
22
|
+
gem.add_dependency "capistrano", "~> 2.13.5"
|
23
|
+
gem.add_dependency "rvm-capistrano", "~> 1.2.7"
|
24
|
+
|
25
|
+
gem.files = `git ls-files`.split($/)
|
26
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
27
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
28
|
+
gem.require_paths = ["lib"]
|
29
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require "capistrano"
|
2
|
+
|
3
|
+
module Deploify
|
4
|
+
|
5
|
+
DEPLOIFY_TEMPLATES_BASE = File.join(File.dirname(__FILE__), "templates")
|
6
|
+
|
7
|
+
# Render template (usually a config file)
|
8
|
+
#
|
9
|
+
# Usually we render it to a file on the local filesystem.
|
10
|
+
# This way, we keep a copy of the config file under source control.
|
11
|
+
# We can make manual changes if required and push to new hosts.
|
12
|
+
#
|
13
|
+
# If the options hash contains :path then it's written to that path.
|
14
|
+
# If it contains :remote => true, the file will instead be written to remote targets
|
15
|
+
# If options[:path] and options[:remote] are missing, it just returns the rendered
|
16
|
+
# template as a string (good for debugging).
|
17
|
+
#
|
18
|
+
# XXX I would like to get rid of :render_template_to_file
|
19
|
+
# XXX Perhaps pass an option to this function to write to remote
|
20
|
+
#
|
21
|
+
def render_template(app, options={})
|
22
|
+
template = options[:template]
|
23
|
+
path = options[:path] || nil
|
24
|
+
remote = options[:remote] || false
|
25
|
+
mode = options[:mode] || 0755
|
26
|
+
owner = options[:owner] || nil
|
27
|
+
stage = exists?(:stage) ? fetch(:stage).to_s : ''
|
28
|
+
# replace this with a check for the file
|
29
|
+
unless template
|
30
|
+
puts "render_template() requires a value for the template!"
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
# If local copies of deploifyy templates exist they will be used
|
35
|
+
# If you don't specify the location with the local_template_dir option
|
36
|
+
# it defaults to config/templates.
|
37
|
+
# e.g. config/templates/nginx/nginx.conf.erb
|
38
|
+
local_template = File.join(local_template_dir, app.to_s, template)
|
39
|
+
if File.exists?(local_template)
|
40
|
+
puts
|
41
|
+
puts "Using local template (#{local_template})"
|
42
|
+
template = ERB.new(IO.read(local_template), nil, '-')
|
43
|
+
else
|
44
|
+
template = ERB.new(IO.read(File.join(DEPLOIFY_TEMPLATES_BASE, app.to_s, template)), nil, '-')
|
45
|
+
end
|
46
|
+
rendered_template = template.result(binding)
|
47
|
+
|
48
|
+
if remote
|
49
|
+
# render to remote machine
|
50
|
+
puts 'You need to specify a path to render the template to!' unless path
|
51
|
+
exit unless path
|
52
|
+
sudo "test -d #{File.dirname(path)} || #{sudo} mkdir -p #{File.dirname(path)}"
|
53
|
+
std.su_put rendered_template, path, '/tmp/', :mode => mode
|
54
|
+
sudo "chown #{owner} #{path}" if defined?(owner)
|
55
|
+
elsif path
|
56
|
+
# render to local file
|
57
|
+
full_path = File.join('config', stage, app.to_s, path)
|
58
|
+
path_dir = File.dirname(File.expand_path(full_path))
|
59
|
+
if File.exists?(full_path)
|
60
|
+
if IO.read(full_path) == rendered_template
|
61
|
+
puts "[skip] Identical file exists (#{full_path})."
|
62
|
+
return false
|
63
|
+
elsif overwrite?(full_path, rendered_template)
|
64
|
+
File.delete(full_path)
|
65
|
+
else
|
66
|
+
puts "[skip] Not overwriting #{full_path}"
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
FileUtils.mkdir_p "#{path_dir}" unless File.directory?(path_dir)
|
71
|
+
# added line above to make windows compatible
|
72
|
+
# system "mkdir -p #{path_dir}" if ! File.directory?(path_dir)
|
73
|
+
File.open(File.expand_path(full_path), 'w') { |f| f.write rendered_template }
|
74
|
+
puts "[done] #{full_path} written"
|
75
|
+
else
|
76
|
+
# render to string
|
77
|
+
return rendered_template
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Copy configs to server(s). Note there is no :pull task. No changes should
|
82
|
+
# be made to configs on the servers so why would you need to pull them back?
|
83
|
+
def push_configs(app, files)
|
84
|
+
app = app.to_s
|
85
|
+
stage = exists?(:stage) ? fetch(:stage).to_s : ''
|
86
|
+
|
87
|
+
files.each do |file|
|
88
|
+
full_local_path = File.join('config', stage, app, file[:path])
|
89
|
+
if File.exists?(full_local_path)
|
90
|
+
# If the file path is relative we will prepend a path to this projects
|
91
|
+
# own config directory for this service.
|
92
|
+
if file[:path][0, 1] != '/'
|
93
|
+
full_remote_path = File.join(deploy_to, app, file[:path])
|
94
|
+
else
|
95
|
+
full_remote_path = file[:path]
|
96
|
+
end
|
97
|
+
sudo "test -d #{File.dirname(full_remote_path)} || #{sudo} mkdir -p #{File.dirname(full_remote_path)}"
|
98
|
+
std.su_put File.read(full_local_path), full_remote_path, '/tmp/', :mode => file[:mode]
|
99
|
+
sudo "chown #{file[:owner]} #{full_remote_path}"
|
100
|
+
else
|
101
|
+
# Render directly to remote host.
|
102
|
+
render_template(app, file.merge(:remote => true))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def overwrite?(full_path, rendered_template)
|
108
|
+
if defined?(overwrite_all)
|
109
|
+
return overwrite_all ? true : false
|
110
|
+
end
|
111
|
+
|
112
|
+
puts
|
113
|
+
response = Capistrano::CLI.ui.ask "File exists (#{full_path}).
|
114
|
+
Overwrite? ([y]es, [n]o, [d]iff)" do |q|
|
115
|
+
q.default = 'n'
|
116
|
+
end
|
117
|
+
|
118
|
+
case response
|
119
|
+
when 'y'
|
120
|
+
return true
|
121
|
+
when 'n'
|
122
|
+
return false
|
123
|
+
when 'd'
|
124
|
+
require 'tempfile'
|
125
|
+
tf = Tempfile.new("deprec_diff")
|
126
|
+
tf.puts(rendered_template)
|
127
|
+
tf.close
|
128
|
+
puts
|
129
|
+
puts "Running diff -u current_file new_file_if_you_overwrite"
|
130
|
+
puts
|
131
|
+
system "diff -u #{full_path} #{tf.path} | less"
|
132
|
+
puts
|
133
|
+
overwrite?(full_path, rendered_template)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# create new user account on target system
|
138
|
+
def useradd(user, options={})
|
139
|
+
options[:shell] ||= "/bin/bash" # new accounts on ubuntu have been getting /bin/sh
|
140
|
+
switches = ''
|
141
|
+
switches += " --shell=#{options[:shell]} " if options[:shell]
|
142
|
+
unless options[:homedir] == false
|
143
|
+
switches += " --create-home "
|
144
|
+
switches += " --home #{options[:homedir]} " if options[:homedir]
|
145
|
+
end
|
146
|
+
switches += " --gid #{options[:group]} " unless options[:group].nil?
|
147
|
+
invoke_command "#{sudo} mkdir -p #{File.dirname(options[:homedir])}" if options[:homedir]
|
148
|
+
invoke_command "grep '^#{user}:' /etc/passwd || #{sudo} /usr/sbin/useradd #{switches} #{user}", :via => run_method
|
149
|
+
end
|
150
|
+
|
151
|
+
# create a new group on target system
|
152
|
+
def groupadd(group, options={})
|
153
|
+
via = options.delete(:via) || run_method
|
154
|
+
invoke_command "grep '#{group}:' /etc/group || #{sudo} /usr/sbin/groupadd #{group}", :via => via
|
155
|
+
end
|
156
|
+
|
157
|
+
# add group to the list of groups this user belongs to
|
158
|
+
def add_user_to_group(user, group)
|
159
|
+
invoke_command "groups #{user} | grep ' #{group} ' || #{sudo} /usr/sbin/usermod -G #{group} -a #{user}", :via => run_method
|
160
|
+
end
|
161
|
+
|
162
|
+
# create directory if it doesn't already exist
|
163
|
+
# set permissions and ownership
|
164
|
+
# XXX move mode, path and
|
165
|
+
def mkdir(path, options={})
|
166
|
+
via = options.delete(:via) || :run
|
167
|
+
# XXX need to make sudo commands wrap the whole command (sh -c ?)
|
168
|
+
# XXX removed the extra 'sudo' from after the '||' - need something else
|
169
|
+
invoke_command "test -d #{path} || #{sudo if via == :sudo} mkdir -p #{path}"
|
170
|
+
invoke_command "chmod #{sprintf("%3o",options[:mode] || 0755)} #{path}", :via => via if options[:mode]
|
171
|
+
invoke_command "chown -R #{options[:owner]} #{path}", :via => via if options[:owner]
|
172
|
+
groupadd(options[:group], :via => via) if options[:group]
|
173
|
+
invoke_command "chgrp -R #{options[:group]} #{path}", :via => via if options[:group]
|
174
|
+
end
|
175
|
+
|
176
|
+
def install_deps(packages=[])
|
177
|
+
apt.install({:base => Array(packages)}, :stable)
|
178
|
+
end
|
179
|
+
|
180
|
+
def teardown_connections
|
181
|
+
sessions.keys.each do |server|
|
182
|
+
sessions[server].close
|
183
|
+
sessions.delete(server)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
Capistrano.plugin :_deploify, Deploify
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# Copyright 2012 by Richard Riman. All rights reserved.
|
2
|
+
|
3
|
+
require "bundler/capistrano"
|
4
|
+
require "rvm/capistrano"
|
5
|
+
|
6
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
7
|
+
|
8
|
+
# Set the value if not already set
|
9
|
+
# This method is accessible to all recipe files
|
10
|
+
# Defined and used by capistrano/deploy tasks
|
11
|
+
def _cset(name, *args, &block)
|
12
|
+
unless exists?(name)
|
13
|
+
set(name, *args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
alias :default :_cset
|
18
|
+
|
19
|
+
# we allways use bundler, so change rake call
|
20
|
+
_cset :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
|
21
|
+
|
22
|
+
default_run_options[:pty] = true
|
23
|
+
|
24
|
+
set :local_template_dir, File.join("config", "templates")
|
25
|
+
|
26
|
+
# The following two Constants contain details of the configuration
|
27
|
+
# files used by each service. They're used when generating config
|
28
|
+
# files from templates and when configs files are pushed out to servers.
|
29
|
+
#
|
30
|
+
# They are populated by the recipe file for each service
|
31
|
+
#
|
32
|
+
SYSTEM_CONFIG_FILES = {} # e.g. httpd.conf
|
33
|
+
PROJECT_CONFIG_FILES = {} # e.g. projectname-httpd-vhost.conf
|
34
|
+
|
35
|
+
# deploify defines some generic recipes for common services
|
36
|
+
# including web, app and database servers
|
37
|
+
#
|
38
|
+
# They default to my current favourites which you can override
|
39
|
+
#
|
40
|
+
# Service options
|
41
|
+
CHOICES_WEBSERVER = [:nginx]
|
42
|
+
CHOICES_APPSERVER = [:passenger, :thin] # :puma, :unicorn
|
43
|
+
CHOICES_DATABASE = [:mysql] # :mongodb
|
44
|
+
#
|
45
|
+
# Service defaults
|
46
|
+
set :web_server_type, :nginx
|
47
|
+
set :app_server_type, :passenger
|
48
|
+
set :db_server_type, :mysql
|
49
|
+
|
50
|
+
# we always use RMV, but we must set :system, because RVM default is :user
|
51
|
+
set :rvm_type, :system
|
52
|
+
set(:rvm_ruby_string) do
|
53
|
+
Capistrano::CLI.ui.choose do |menu|
|
54
|
+
menu.prompt = "Please choose RVM Ruby version for this application"
|
55
|
+
menu.choices("1.8.6", "1.8.7", "ree", "1.9.2", "1.9.3")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
set :use_monit, true
|
60
|
+
|
61
|
+
# Prompt user for missing values if not supplied
|
62
|
+
set(:application) do
|
63
|
+
Capistrano::CLI.ui.ask "Enter name of project (no spaces)" do |q|
|
64
|
+
q.validate = /^[0-9a-z_]*$/
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
set(:domain) do
|
69
|
+
Capistrano::CLI.ui.ask "Enter domain name for project" do |q|
|
70
|
+
q.validate = /^[0-9a-z_\-\.]*$/
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# some tasks run commands requiring special user privileges on remote servers
|
75
|
+
# these tasks will run the commands with:
|
76
|
+
# :invoke_command "command", :via => run_method
|
77
|
+
# override this value if sudo is not an option
|
78
|
+
# in that case, your use will need the correct privileges
|
79
|
+
set :run_method, :sudo
|
80
|
+
|
81
|
+
# deploy from TAG stuff
|
82
|
+
|
83
|
+
set :deployable_without_tag, false # only used WITHOUT multi-staging
|
84
|
+
set :stages, %w(production staging) # default stages (only used WITH multi-staging)
|
85
|
+
set :stages_deployable_without_tag, %w(staging) # only used WITH multi-staging
|
86
|
+
|
87
|
+
set(:branch) do
|
88
|
+
unless exists?(:stage)
|
89
|
+
# single stage configuration
|
90
|
+
if deployable_without_tag
|
91
|
+
tag = `git branch | grep '*' | awk '{ print $2 }'`.split[0]
|
92
|
+
else
|
93
|
+
tag = Capistrano::CLI.ui.ask("TAG to deploy (make sure to push the tag first): ")
|
94
|
+
end
|
95
|
+
else
|
96
|
+
# multi-staging configuration
|
97
|
+
if stages_deployable_without_tag.include?(fetch(:stage).to_s)
|
98
|
+
tag = `git branch | grep '*' | awk '{ print $2 }'`.split[0]
|
99
|
+
else
|
100
|
+
tag = Capistrano::CLI.ui.ask("TAG from which we deploy on stage '#{fetch(:stage)}' (make sure to push the tag first): ")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# rails deploy stuff
|
106
|
+
set :apps_root, "/var/www" # parent dir for apps
|
107
|
+
set(:deploy_to) { File.join(apps_root, application) } # dir for current app
|
108
|
+
set(:current_path) { File.join(deploy_to, "current") }
|
109
|
+
set(:shared_path) { File.join(deploy_to, "shared") }
|
110
|
+
|
111
|
+
# more rails deploy stuff
|
112
|
+
set :user, "deploy" # user who is deploying
|
113
|
+
set :group, "deploy" # deployment group
|
114
|
+
set(:web_server_aliases) { domain.match(/^www/) ? [] : ["www.#{domain}"] }
|
115
|
+
|
116
|
+
# rails deploy GIT stuff
|
117
|
+
set :scm, :git
|
118
|
+
set :deploy_via, :remote_cache
|
119
|
+
set :copy_exclude, ['.git', '.rvmrc']
|
120
|
+
set(:repository) do
|
121
|
+
Capistrano::CLI.ui.ask "Enter repository URL for project"
|
122
|
+
end
|
123
|
+
|
124
|
+
# webserver stuff
|
125
|
+
set :force_domain_with_www, false
|
126
|
+
|
127
|
+
on :load, "deploify:connect_canonical_tasks"
|
128
|
+
|
129
|
+
namespace :deploify do
|
130
|
+
|
131
|
+
task :connect_canonical_tasks do
|
132
|
+
# link application specific recipes into canonical task names
|
133
|
+
# e.g. deprec:web:restart => deprec:nginx:restart
|
134
|
+
namespaces_to_connect = {
|
135
|
+
:web => :web_server_type,
|
136
|
+
:app => :app_server_type,
|
137
|
+
:db => :db_server_type
|
138
|
+
}
|
139
|
+
metaclass = class << self; self; end
|
140
|
+
namespaces_to_connect.each do |server, choice|
|
141
|
+
server_type = send(choice).to_sym
|
142
|
+
if server_type != :none
|
143
|
+
metaclass.send(:define_method, server) { namespaces[server] }
|
144
|
+
namespaces[server] = deploify.send(server_type)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end # namespace :deploify
|
150
|
+
|
151
|
+
namespace :deploy do
|
152
|
+
|
153
|
+
task :start, :roles => :app, :except => { :no_release => true } do
|
154
|
+
run "#{sudo} service #{app_server_type}-#{application} start"
|
155
|
+
end
|
156
|
+
|
157
|
+
task :stop, :roles => :app, :except => { :no_release => true } do
|
158
|
+
run "#{sudo} service #{app_server_type}-#{application} stop"
|
159
|
+
end
|
160
|
+
|
161
|
+
task :restart, :roles => :app, :except => { :no_release => true } do
|
162
|
+
run "#{sudo} service #{app_server_type}-#{application} graceful"
|
163
|
+
end
|
164
|
+
|
165
|
+
task :force_restart, :roles => :app, :except => { :no_release => true } do
|
166
|
+
run "#{sudo} service #{app_server_type}-#{application} restart"
|
167
|
+
end
|
168
|
+
|
169
|
+
end # namespace :deploy
|
170
|
+
|
171
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2012 by Richard Riman. All rights reserved.
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
|
5
|
+
# we use monit primary to control passenger processes so the tasks
|
6
|
+
# are restricted to :app. You may use it for other processes.
|
7
|
+
# In this case, specify HOSTS=hostname on the command line or use:
|
8
|
+
# for_roles(:role_name) { top.deploify.monit.task_name }
|
9
|
+
# in your recipes.
|
10
|
+
|
11
|
+
set :monit_timeout_interval, 120
|
12
|
+
set :monit_conf_dir, "/etc/monit/conf.d"
|
13
|
+
# TODO
|
14
|
+
# set :monit_alert_recipients, %w(monitoring@example.com)
|
15
|
+
# set :monit_timeout_recipients, %w(monitoring@example.com)
|
16
|
+
|
17
|
+
namespace :deploify do
|
18
|
+
|
19
|
+
namespace :monit do
|
20
|
+
|
21
|
+
PROJECT_CONFIG_FILES[:monit] = [
|
22
|
+
{ :template => "monit.conf.erb",
|
23
|
+
:path => "monit.conf",
|
24
|
+
:mode => 0644,
|
25
|
+
:owner => "root:root" }
|
26
|
+
]
|
27
|
+
|
28
|
+
desc <<-DESC
|
29
|
+
Generate monit application config from template. Note that this does not
|
30
|
+
push the config to the server, it merely generates required
|
31
|
+
configuration files. These should be kept under source control.
|
32
|
+
DESC
|
33
|
+
task :config_gen_project do
|
34
|
+
PROJECT_CONFIG_FILES[:monit].each do |file|
|
35
|
+
_deploify.render_template(:monit, file)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Push application monit config files to server"
|
40
|
+
task :config_project, :roles => :app do
|
41
|
+
_deploify.push_configs(:monit, PROJECT_CONFIG_FILES[:monit])
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Start Monit"
|
45
|
+
task :start, :roles => :app do
|
46
|
+
send(run_method, "service monit start")
|
47
|
+
end
|
48
|
+
|
49
|
+
task :activate do
|
50
|
+
top.deploify.monit.activate_project
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Activate application monit config and reload monit"
|
54
|
+
task :activate_project, :roles => :app do
|
55
|
+
run "#{sudo} ln -sf #{deploy_to}/monit/monit.conf #{monit_conf_dir}/#{application}"
|
56
|
+
top.deploify.monit.reload
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Dectivate application monit config and reload monit"
|
60
|
+
task :deactivate_project, :roles => :app do
|
61
|
+
run "#{sudo} rm -f #{monit_conf_dir}/#{application}; exit 0"
|
62
|
+
top.deploify.monit.reload
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Stop Monit"
|
66
|
+
task :stop, :roles => :app do
|
67
|
+
send(run_method, "service monit stop")
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Restart Monit"
|
71
|
+
task :restart, :roles => :app do
|
72
|
+
send(run_method, "service monit restart")
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "Reload Monit"
|
76
|
+
task :reload, :roles => :app do
|
77
|
+
send(run_method, "service monit restart")
|
78
|
+
end
|
79
|
+
|
80
|
+
end # namespace :monit
|
81
|
+
|
82
|
+
end # namespace :deploify
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright 2006-2008 by Mike Bailey. All rights reserved.
|
2
|
+
# Copyright 2012 by Richard Riman. All rights reserved.
|
3
|
+
|
4
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
5
|
+
|
6
|
+
set :mysql_admin_user, "deploy"
|
7
|
+
set(:mysql_admin_pass) { Capistrano::CLI.password_prompt "Enter database password for '#{mysql_admin_user}':" }
|
8
|
+
|
9
|
+
namespace :deploify do
|
10
|
+
|
11
|
+
namespace :mysql do
|
12
|
+
|
13
|
+
desc "Start Mysql"
|
14
|
+
task :start, :roles => :db do
|
15
|
+
send(run_method, "service mysql start")
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Stop Mysql"
|
19
|
+
task :stop, :roles => :db do
|
20
|
+
send(run_method, "service mysql stop")
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Restart Mysql"
|
24
|
+
task :restart, :roles => :db do
|
25
|
+
send(run_method, "service mysql restart")
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Reload Mysql"
|
29
|
+
task :reload, :roles => :db do
|
30
|
+
send(run_method, "service mysql reload")
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Create a database"
|
34
|
+
task :create_database, :roles => :db do
|
35
|
+
cmd = "CREATE DATABASE IF NOT EXISTS #{db_name}"
|
36
|
+
run "mysql -u #{mysql_admin_user} -p -e '#{cmd}'" do |channel, stream, data|
|
37
|
+
if data =~ /^Enter password:/
|
38
|
+
channel.send_data "#{mysql_admin_pass}\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Grant user access to database"
|
44
|
+
task :grant_user_access_to_database, :roles => :db do
|
45
|
+
cmd = "GRANT ALL PRIVILEGES ON #{db_name}.* TO '#{db_user}'@localhost IDENTIFIED BY '#{db_password}';"
|
46
|
+
run "mysql -u #{mysql_admin_user} -p #{db_name} -e \"#{cmd}\"" do |channel, stream, data|
|
47
|
+
if data =~ /^Enter password:/
|
48
|
+
channel.send_data "#{mysql_admin_pass}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # namespace :mysql
|
54
|
+
|
55
|
+
end # namespace :deploify
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Setup replication
|
61
|
+
#
|
62
|
+
|
63
|
+
# setup user for repl
|
64
|
+
# GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%.yourdomain.com' IDENTIFIED BY 'slavepass';
|
65
|
+
|
66
|
+
# get current position of binlog
|
67
|
+
# mysql> FLUSH TABLES WITH READ LOCK;
|
68
|
+
# Query OK, 0 rows affected (0.00 sec)
|
69
|
+
#
|
70
|
+
# mysql> SHOW MASTER STATUS;
|
71
|
+
# +------------------+----------+--------------+------------------+
|
72
|
+
# | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
|
73
|
+
# +------------------+----------+--------------+------------------+
|
74
|
+
# | mysql-bin.000012 | 296 | | |
|
75
|
+
# +------------------+----------+--------------+------------------+
|
76
|
+
# 1 row in set (0.00 sec)
|
77
|
+
#
|
78
|
+
# # get current data
|
79
|
+
# mysqldump --all-databases --master-data >dbdump.db
|
80
|
+
#
|
81
|
+
# UNLOCK TABLES;
|
82
|
+
|
83
|
+
|
84
|
+
# Replication Features and Issues
|
85
|
+
# http://dev.mysql.com/doc/refman/5.0/en/replication-features.html
|