matross 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +24 -15
- data/lib/matross/mysql.rb +99 -11
- data/lib/matross/templates/mysql/backup.sh.erb +28 -0
- data/lib/matross/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDdiOTQ1YzRjMmRlOTRhZDk3YTdlNzc1NDhkMjhjMTJjNjY4YjYyOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZDhlYmYxZjhhODE2MTM5ODkxODA0NmFlNzkwZTRhOWIxMGQyODNmZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NTYzZTk0ZDAzZmU1ODhiYWJiYWNlOWZjMDBjN2FhYTk0ZmVmMzg0YTIwMmVh
|
10
|
+
ZDA2NTRiYzE0M2ZiOTExODNjNmNlM2NjNzFiN2RjZDUyMDBhYTIzNGEwNTVl
|
11
|
+
MjNjOTZkNzQzYzNjZjVhYjk5YzEyMTQ2ZjI2OTAzOWVmMTZkZjU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTY5NmNhN2M0NTI2NTk4NzVhNTY1OTM5YjA2OWFhZGQzN2Q4YjY2YzMwNGEw
|
14
|
+
YTRkOTZlZWU5MWM0NTE3Y2FiYjBmN2ViYWZmYmU2NTAyODdjMzkzZjAwMzcy
|
15
|
+
OTZlZWUyYTJjZmRlMzhmYTMxYWQ1NjQ0M2VhMmVmZDk1OWQ2MTk=
|
data/README.md
CHANGED
@@ -130,31 +130,41 @@ This recipes creates and configures the virtual_host for the application. [This
|
|
130
130
|
| `nginx:lock` | Sets up the a basic http auth on the virtual host |
|
131
131
|
| `nginx:unlock` | Removes the basic http auth |
|
132
132
|
|
133
|
-
|
134
133
|
### MySQL
|
135
134
|
|
136
135
|
Requires having [`mysql2`](http://rubygems.org/gems/mysql2) available in the application. In our MySQL recipe we dynamically generate a `database.yml` based on the variables that should be set globally or per-stage.
|
137
136
|
|
137
|
+
The backup routine requires [`s3cmd`](http://s3tools.org/s3cmd) installed and properly configured. The key pair must have write access to the backup bucket on S3.
|
138
|
+
|
138
139
|
Overwritable template: [`database.yml.erb`](lib/matross/templates/mysql/database.yml.erb)
|
139
140
|
|
140
141
|
> Variables
|
141
142
|
|
142
|
-
| Variable
|
143
|
-
| ---
|
144
|
-
| `:database_config`
|
145
|
-
| `:mysql_host`
|
146
|
-
| `:mysql_database`
|
147
|
-
| `:mysql_user`
|
148
|
-
| `:mysql_passwd`
|
143
|
+
| Variable | Default value | Description |
|
144
|
+
| --- | --- | --- |
|
145
|
+
| `:database_config` | `"#{shared_path}/config/database.yml"` | Location of the configuration file |
|
146
|
+
| `:mysql_host` | None | MySQL host address |
|
147
|
+
| `:mysql_database` | None | MySQL database name. Dashes `-` are *gsubed* for underscores `_` |
|
148
|
+
| `:mysql_user` | None | MySQL user |
|
149
|
+
| `:mysql_passwd` | None | MySQL password |
|
150
|
+
| `:mysql_backup_script` | `"#{shared_path}/matross/mysql_backup.sh"` | MySQL backup script location |
|
151
|
+
| `:mysql_backup_cron_schedule` | `'30 3 * * *'` | Cron schedule for the backup script |
|
152
|
+
| `:mysql_backup_bucket` | None | Bucket used to store the dumps |
|
153
|
+
| `:mysql_backup_prefix` | None | Flat file prefix for the backup files |
|
149
154
|
|
150
155
|
> Tasks
|
151
156
|
|
152
|
-
| Task
|
153
|
-
| ---
|
154
|
-
| `mysql:setup`
|
155
|
-
| `mysql:symlink`
|
156
|
-
| `mysql:create`
|
157
|
-
| `mysql:schema_load`
|
157
|
+
| Task | Description |
|
158
|
+
| --- | --- |
|
159
|
+
| `mysql:setup` | Creates the `database.yml` in the `shared_path` |
|
160
|
+
| `mysql:symlink` | Creates a symlink for the `database.yml` file in the `current_path` |
|
161
|
+
| `mysql:create` | Creates the database if it hasn't been created |
|
162
|
+
| `mysql:schema_load` | Loads the schema if there are no tables in the DB |
|
163
|
+
| `mysql:backup:setup` | Creates the backup script and configures the user's cron - *Not hooked to any other task* |
|
164
|
+
| `mysql:dump:do` | Dumps the application database |
|
165
|
+
| `mysql:dump:get` | Downloads a copy of the last generated database dump |
|
166
|
+
| `mysql:dump:apply` | Apply the latest dump generated stored in 'dumps' locally |
|
167
|
+
|
158
168
|
|
159
169
|
## Mongoid
|
160
170
|
|
@@ -198,7 +208,6 @@ Procfile task: `dj: bundle exec rake jobs:work` or `dj_<%= queue_name %>: bundle
|
|
198
208
|
| --- | --- |
|
199
209
|
| `delayed_job:procfile` | Defines how `delayed_job` should be run in a temporary `Procfile` |
|
200
210
|
|
201
|
-
|
202
211
|
### Fog (AWS)
|
203
212
|
|
204
213
|
Requires having [`fog`](http://rubygems.org/gems/fog) available in the application. When we use `fog`, it is for interacting with Amazon services, once again very opinionated.
|
data/lib/matross/mysql.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
dep_included? 'mysql2'
|
2
2
|
|
3
|
-
_cset(:database_config)
|
3
|
+
_cset(:database_config) { "#{shared_path}/config/database.yml" }
|
4
|
+
_cset(:mysql_backup_script) { "#{shared_path}/matross/mysql_backup.sh" }
|
5
|
+
_cset :mysql_backup_cron_schedule, '30 3 * * *'
|
4
6
|
|
5
7
|
namespace :mysql do
|
6
8
|
|
@@ -18,26 +20,112 @@ namespace :mysql do
|
|
18
20
|
after "bundle:install", "mysql:symlink"
|
19
21
|
|
20
22
|
desc "Creates the application database"
|
21
|
-
task :create, :roles =>
|
23
|
+
task :create, :roles => :db do
|
22
24
|
sql = <<-EOF.gsub(/^\s+/, '')
|
23
25
|
CREATE DATABASE IF NOT EXISTS #{mysql_database.gsub("-", "_")};
|
24
26
|
EOF
|
25
|
-
run
|
27
|
+
run %W{mysql --user=#{mysql_user}
|
28
|
+
#{'--password=' + mysql_passwd unless mysql_passwd.empty?}
|
29
|
+
--host=#{mysql_host}
|
30
|
+
--execute="#{sql}"} * ' '
|
26
31
|
end
|
27
32
|
after "mysql:setup", "mysql:create"
|
28
33
|
|
29
34
|
desc "Loads the application schema into the database"
|
30
|
-
task :schema_load, :roles =>
|
35
|
+
task :schema_load, :roles => :db do
|
31
36
|
sql = <<-EOF.gsub(/^\s+/, '')
|
32
37
|
SELECT count(*) FROM information_schema.TABLES WHERE (TABLE_SCHEMA = '#{mysql_database.gsub("-", "_")}');
|
33
38
|
EOF
|
34
|
-
table_count = capture(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
run
|
40
|
-
|
39
|
+
table_count = capture(%W{mysql --batch --skip-column-names
|
40
|
+
--user=#{mysql_user}
|
41
|
+
#{'--password=' + mysql_passwd unless mysql_passwd.empty?}
|
42
|
+
--host=#{mysql_host}
|
43
|
+
--execute="#{sql}"} * ' ').to_i
|
44
|
+
run %W{cd #{release_path} &&
|
45
|
+
RAILS_ENV=#{rails_env.to_s.shellescape} bundle exec rake db:schema:load
|
46
|
+
} * ' ' if table_count == 0
|
41
47
|
end
|
42
48
|
after "mysql:symlink", "mysql:schema_load"
|
49
|
+
|
50
|
+
namespace :backup do
|
51
|
+
|
52
|
+
# This routine is heavily inspired by whenever's approach
|
53
|
+
# https://github.com/javan/whenever
|
54
|
+
desc "Updates the crontab"
|
55
|
+
task :setup, :roles => :db do
|
56
|
+
template "mysql/backup.sh.erb", mysql_backup_script
|
57
|
+
run "chmod +x #{mysql_backup_script}"
|
58
|
+
|
59
|
+
comment_open = '# Begin Matross generated task for MySQL Backup'
|
60
|
+
comment_close = '# End Matross generated task for MySQL Backup'
|
61
|
+
|
62
|
+
cron_command = "#{mysql_backup_script} 2>&1 >> #{shared_path}/log/mysql_backup.log"
|
63
|
+
cron_entry = "#{mysql_backup_cron_schedule} #{cron_command}"
|
64
|
+
cron = [comment_open, cron_entry, comment_close].compact.join("\n")
|
65
|
+
|
66
|
+
current_crontab = ''
|
67
|
+
begin
|
68
|
+
# Some cron implementations require all non-comment lines to be
|
69
|
+
# newline-terminated. Strip all newlines and replace with the default
|
70
|
+
# platform record seperator ($/)
|
71
|
+
current_crontab = capture("crontab -l -u #{user} 2> /dev/null").gsub!(/\s+$/, $/)
|
72
|
+
rescue Capistrano::CommandError
|
73
|
+
logger.debug 'The user has no crontab'
|
74
|
+
end
|
75
|
+
contains_open_comment = current_crontab =~ /^#{comment_open}\s*$/
|
76
|
+
contains_close_comment = current_crontab =~ /^#{comment_close}\s*$/
|
77
|
+
|
78
|
+
# If an existing identier block is found, replace it with the new cron entries
|
79
|
+
if contains_open_comment && contains_close_comment
|
80
|
+
updated_crontab = current_crontab.gsub(/^#{comment_open}\s*$.+^#{comment_close}\s*$/m, cron.chomp)
|
81
|
+
else # Otherwise, append the new cron entries after any existing ones
|
82
|
+
updated_crontab = current_crontab.empty? ? cron : [current_crontab, cron].join("\n")
|
83
|
+
end.gsub(/\n{2,}/, "\n") # More than one newline becomes just one.
|
84
|
+
|
85
|
+
temp_crontab_file = "/tmp/matross_#{user}_crontab"
|
86
|
+
put updated_crontab, temp_crontab_file
|
87
|
+
run "crontab -u #{user} #{temp_crontab_file}"
|
88
|
+
run "rm #{temp_crontab_file}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
namespace :dump do
|
93
|
+
|
94
|
+
desc "Dumps the application database"
|
95
|
+
task :do, :roles => :db, :except => { :no_release => true } do
|
96
|
+
run "mkdir -p #{shared_path}/dumps"
|
97
|
+
run %W{cd #{shared_path}/dumps &&
|
98
|
+
mysqldump --quote-names --create-options
|
99
|
+
--user=#{mysql_user}
|
100
|
+
#{'--password=' + mysql_passwd unless mysql_passwd.empty?}
|
101
|
+
--host=#{mysql_host}
|
102
|
+
#{mysql_database} |
|
103
|
+
gzip > "$(date +'#{mysql_database}_\%Y\%m\%d\%H\%M.sql.gz')"} * ' '
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "Downloads a copy of the last generated database dump"
|
107
|
+
task :get, :roles => :db, :except => { :no_release => true } do
|
108
|
+
run_locally "mkdir -p dumps"
|
109
|
+
most_recent_bkp = capture(%W{find #{shared_path} -type f -name
|
110
|
+
'#{mysql_database}_*.sql.gz'} * ' '
|
111
|
+
).split.sort.last
|
112
|
+
abort "No dump found. Run mysql:dump:do." if most_recent_bkp.nil?
|
113
|
+
|
114
|
+
download most_recent_bkp, "dumps", :via => :scp
|
115
|
+
run_locally "gzip -d dumps/#{File.basename(most_recent_bkp)}"
|
116
|
+
end
|
117
|
+
|
118
|
+
desc "Apply the latest dump generated stored in 'dumps' locally"
|
119
|
+
task :apply, :roles => :db, :except => { :no_release => true } do
|
120
|
+
most_recent_bkp = %x[find dumps -type f -name\
|
121
|
+
'#{mysql_database}_*.sql'].split.sort.last
|
122
|
+
abort "No dump found. Run mysql:dump:get." if most_recent_bkp.nil?
|
123
|
+
|
124
|
+
db_config = YAML.load(File.read('config/database.yml'))['development']
|
125
|
+
run_locally %W{mysql --user=#{db_config['username']}
|
126
|
+
--host=#{db_config['host']}
|
127
|
+
#{'--password=' + db_config['password'] unless db_config['password'].nil?}
|
128
|
+
#{db_config['database']} < #{most_recent_bkp}} * ' '
|
129
|
+
end
|
130
|
+
end
|
43
131
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
SRCDIR='/tmp/<%= application %>-mysql-backup'
|
4
|
+
BUCKET=<%= mysql_backup_bucket %>
|
5
|
+
PREFIX=<%= File.join(mysql_backup_prefix, '') %>
|
6
|
+
|
7
|
+
# database access details
|
8
|
+
HOST=<%= mysql_host %>
|
9
|
+
USER=<%= mysql_user%>
|
10
|
+
PASS=<%= mysql_passwd%>
|
11
|
+
DB=<%= mysql_database%>
|
12
|
+
|
13
|
+
# backup name
|
14
|
+
BACKUP_NAME="$(date +'bkp%Y%m%d%H%M')"
|
15
|
+
|
16
|
+
# make the temp directory if it doesn't exist and cd into it
|
17
|
+
mkdir -p ${SRCDIR}
|
18
|
+
cd ${SRCDIR}
|
19
|
+
|
20
|
+
# mysql dump
|
21
|
+
mysqldump -h${HOST} -u${USER} --force ${DB} > ${BACKUP_NAME}.sql
|
22
|
+
tar --create --gzip --absolute-names --file ${BACKUP_NAME}.tar.gz ${BACKUP_NAME}.sql
|
23
|
+
|
24
|
+
# upload to s3
|
25
|
+
s3cmd put ${SRCDIR}/${BACKUP_NAME}.tar.gz s3://${BUCKET}/${PREFIX}
|
26
|
+
|
27
|
+
# clean old files
|
28
|
+
find ${SRCDIR}/* -mtime +5 -exec rm {} \;
|
data/lib/matross/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matross
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artur Rodrigues
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/matross/templates/fog/fog_config.yml.erb
|
85
85
|
- lib/matross/templates/foreman/process.conf.erb
|
86
86
|
- lib/matross/templates/mongoid/mongoid.yml.erb
|
87
|
+
- lib/matross/templates/mysql/backup.sh.erb
|
87
88
|
- lib/matross/templates/mysql/database.yml.erb
|
88
89
|
- lib/matross/templates/nginx/nginx_virtual_host_conf.erb
|
89
90
|
- lib/matross/templates/unicorn/unicorn.rb.erb
|