deploify 0.2.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 +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
|