matross 0.3.0 → 0.4.0
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.
- 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
|