s3-mysql-backup 1.0.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.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # S3 MySQL Backup
2
+
3
+ Simple backup of a MySQL database to Amazon S3,
4
+ with email notification via Gmail.
5
+
6
+
7
+ ## What does it do?
8
+
9
+ It makes a gzipped and timestamped local backup of the specified
10
+ database using mysqldump. The local backup is then copied to
11
+ Amazon S3, and the results are emailed to the specified recipient.
12
+
13
+ Local and S3 backups are retained at this schedule:
14
+ - keep 30 days complete
15
+ - keep 90 days weekly beyond that
16
+ - keep only monthly after that
17
+
18
+
19
+ ## Configuration
20
+
21
+ Configure with a YAML file:
22
+
23
+ ```yaml
24
+
25
+ # backup_dir where to store the local backups
26
+ backup_dir: ~/s3_mysql_backups
27
+
28
+ # s3_access_key_id your Amazon S3 access_key_id
29
+ # s3_secret_access_key your Amazon S3 secret_access_key
30
+ # s3_bucket your Amazon S3 bucket for the backups
31
+ s3_access_key_id: my-key
32
+ s3_secret_access_key: my-secret
33
+ s3_bucket: my-bucket
34
+
35
+ # dump_user the database user for mysqldump
36
+ # dump_pass the password for the dump user
37
+ dump_user: my-user
38
+ dump_pass: my-pass
39
+
40
+ # mail_to where to send the backup summary email
41
+ mail_to: recipient@example.com
42
+
43
+ # Gmail credentials
44
+ gmail_user: me@gmail.com
45
+ gmail_pass: gmail-password
46
+
47
+ ```
48
+
49
+
50
+ ## Installation
51
+
52
+ gem install s3-mysql-backup
53
+
54
+
55
+ ## Usage
56
+
57
+ S3MysqlBackup.new('database_name', '/path/to/s3-mysql-backup-config.yml').run
58
+
59
+
60
+ ## Credits
61
+
62
+ 2008+ Seventh Compass
@@ -0,0 +1 @@
1
+ require 's3_mysql_backup'
@@ -0,0 +1,108 @@
1
+ require "net/smtp"
2
+ require "time"
3
+ require 'fileutils'
4
+ require 'yaml'
5
+
6
+ require File.dirname(__FILE__) + '/s3utils'
7
+
8
+ #
9
+ class S3MysqlBackup
10
+
11
+ def initialize(db_name, path_to_config)
12
+ @db_name = db_name
13
+ @path_to_config = path_to_config
14
+
15
+ self
16
+ end
17
+
18
+ def run
19
+ ensure_backup_dir_exists
20
+
21
+ @s3utils = S3Utils.new(config['s3_access_key_id'], config['s3_secret_access_key'], config['s3_bucket'])
22
+
23
+ @dumpfile = dump_db
24
+ remove_old_backups
25
+ mail_notification(@dumpfile)
26
+ end
27
+
28
+
29
+ protected
30
+
31
+ # make the DB backup file
32
+ def dump_db
33
+ filename = Time.now.strftime("#{@backup_dir}/#{@db_name}.%Y%m%d.%H%M%S.sql.gz")
34
+ mysqldump = `which mysqldump`.to_s.strip
35
+ `#{mysqldump} --user=#{config['dump_user']} --password=#{config['dump_pass']} #{@db_name} | gzip > #{filename}`
36
+ @s3utils.store(filename)
37
+ filename
38
+ end
39
+
40
+ def ensure_backup_dir_exists
41
+ @backup_dir = File.expand_path(config['backup_dir'])
42
+ FileUtils.mkdir_p @backup_dir
43
+ end
44
+
45
+ def human_size(num, unit='bytes')
46
+ units = %w(bytes KB MB GB TB PB EB ZB YB)
47
+ if num <= 1024
48
+ "#{"%0.2f"%num} #{unit}"
49
+ else
50
+ human_size(num/1024.0, units[units.index(unit)+1])
51
+ end
52
+ end
53
+
54
+ def mail_notification(filename)
55
+ stats = File.stat(filename)
56
+ subject = "sql backup: #{@db_name}: #{human_size(stats.size)}"
57
+
58
+ content = []
59
+ content << "From: #{config['gmail_user']}"
60
+ content << "To: #{config['mail_to']}"
61
+ content << "Subject: #{subject}"
62
+ content << "Date: #{Time.now.rfc2822}"
63
+ content << "\n#{File.basename(filename)}\n" # body
64
+ content = content.join("\n")
65
+
66
+ smtp = Net::SMTP.new("smtp.gmail.com", 587)
67
+ smtp.enable_starttls
68
+ smtp.start("smtp.gmail.com", config['gmail_user'], config['gmail_pass'], :login) do
69
+ smtp.send_message(content, config['gmail_user'], config['mail_to'])
70
+ end
71
+ end
72
+
73
+ def config
74
+ @s3config ||= YAML::load_file(@path_to_config)
75
+ end
76
+
77
+ # remove old backups
78
+ # - keep 30 days complete
79
+ # - keep 90 days weekly beyond that
80
+ # - keep only monthly after that
81
+ def remove_old_backups
82
+ today = Date.today
83
+ weekly = (today - 30)
84
+ monthly = (today - 120)
85
+
86
+ Dir["#{config['backup_dir']}/*.sql.gz"].each do |name|
87
+ date = name.split('.')[1]
88
+ filedate = Date.strptime(date, '%Y%m%d')
89
+
90
+ if filedate < weekly && filedate >= monthly
91
+ # keep weeklies and also first of month
92
+ unless filedate.wday == 0 || filedate.day == 1
93
+ FileUtils.rm_f(name)
94
+ @s3utils.delete(name)
95
+ end
96
+ elsif filedate < monthly
97
+ # delete all old local files
98
+ FileUtils.rm_f(name)
99
+
100
+ # keep just first of month in S3
101
+ unless filedate.day == 1
102
+ @s3utils.delete(name)
103
+ end
104
+ end
105
+ end # Dir.each
106
+ end # remove_old_backups
107
+
108
+ end
data/lib/s3utils.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'aws/s3'
2
+
3
+ include AWS::S3
4
+
5
+
6
+ # wrapper for S3 operations
7
+ #
8
+ class S3Utils
9
+
10
+ def initialize(access_key_id, secret_access_key, bucket)
11
+ @bucket = bucket
12
+ connect(access_key_id, secret_access_key)
13
+ ensure_bucket_exists
14
+ self
15
+ end
16
+
17
+ def copy(from, to)
18
+ S3Object.copy(File.basename(from), File.basename(to), @bucket)
19
+ end
20
+
21
+ def store(file_path)
22
+ S3Object.store(File.basename(file_path), open(file_path), @bucket)
23
+ end
24
+
25
+ def delete(file_path)
26
+ S3Object.delete(File.basename(file_path), @bucket)
27
+ end
28
+
29
+ def list
30
+ Bucket.find(@bucket).objects.each do |obj|
31
+ puts "#{obj.bucket.name}/#{obj.key}"
32
+ end
33
+ end
34
+
35
+ protected
36
+
37
+ def connect(access_key_id, secret_access_key)
38
+ AWS::S3::Base.establish_connection!(
39
+ :access_key_id => access_key_id,
40
+ :secret_access_key => secret_access_key
41
+ )
42
+ end
43
+
44
+ def ensure_bucket_exists
45
+ Bucket.find(@bucket)
46
+
47
+ rescue AWS::S3::NoSuchBucket
48
+ Bucket.create(@bucket)
49
+ end
50
+
51
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3-mysql-backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeff Emminger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-s3
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: A simple mysql backup to Amazon S3
31
+ email: jeff@7compass.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/s3-mysql-backup.rb
37
+ - lib/s3_mysql_backup.rb
38
+ - lib/s3utils.rb
39
+ - README.md
40
+ homepage: https://github.com/7compass/s3-mysql-backup
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - .
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 1.8.24
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Simple mysql backup to S3
65
+ test_files: []