simple_backup 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +2 -1
  4. data/backup_example.rb +23 -13
  5. data/lib/simple_backup/backend/abstract.rb +35 -0
  6. data/lib/simple_backup/backend/local.rb +45 -0
  7. data/lib/simple_backup/backends.rb +60 -0
  8. data/lib/simple_backup/dsl.rb +26 -93
  9. data/lib/simple_backup/engine.rb +41 -3
  10. data/lib/simple_backup/source/abstract.rb +130 -0
  11. data/lib/simple_backup/source/dir.rb +39 -0
  12. data/lib/simple_backup/source/dir_strategy/bare.rb +17 -0
  13. data/lib/simple_backup/source/dir_strategy/capistrano.rb +41 -0
  14. data/lib/simple_backup/source/file.rb +20 -0
  15. data/lib/simple_backup/source/mysql.rb +28 -0
  16. data/lib/simple_backup/sources.rb +74 -0
  17. data/lib/simple_backup/utils/disk_usage.rb +53 -0
  18. data/lib/simple_backup/utils/logger.rb +92 -0
  19. data/lib/simple_backup/utils/mailer.rb +126 -0
  20. data/lib/simple_backup/utils/mysql.rb +65 -0
  21. data/lib/simple_backup/utils.rb +4 -53
  22. data/lib/simple_backup/version.rb +2 -2
  23. data/lib/simple_backup.rb +40 -5
  24. metadata +17 -19
  25. data/lib/simple_backup/engine/abstract.rb +0 -42
  26. data/lib/simple_backup/engine/app_strategy/abstract.rb +0 -13
  27. data/lib/simple_backup/engine/app_strategy/bare.rb +0 -29
  28. data/lib/simple_backup/engine/app_strategy/capistrano.rb +0 -44
  29. data/lib/simple_backup/engine/app_strategy/factory.rb +0 -20
  30. data/lib/simple_backup/engine/apps.rb +0 -63
  31. data/lib/simple_backup/engine/mysql.rb +0 -104
  32. data/lib/simple_backup/exception/app_already_defined.rb +0 -6
  33. data/lib/simple_backup/exception/apps_dir_does_not_exists.rb +0 -6
  34. data/lib/simple_backup/exception/base.rb +0 -6
  35. data/lib/simple_backup/exception/cant_create_dir.rb +0 -6
  36. data/lib/simple_backup/exception/type_does_not_exists.rb +0 -6
  37. data/lib/simple_backup/exception.rb +0 -5
  38. data/lib/simple_backup/logger.rb +0 -84
  39. data/lib/simple_backup/mailer.rb +0 -138
  40. data/lib/simple_backup/storage.rb +0 -96
@@ -1,29 +0,0 @@
1
- module SimpleBackup
2
- module Engine
3
- module AppStrategy
4
- class Bare < Abstract
5
- def backup(name, path, attr)
6
- app_elements = get_path_entries(path).map do |p|
7
- if p.match(/^\.\.?$/)
8
- nil
9
- else
10
- File.join(path, p)
11
- end
12
- end.compact
13
-
14
- @storage.backup do |dir|
15
- FileUtils.cp_r app_elements, dir
16
- end
17
- end
18
-
19
- private
20
- def get_path_entries(path)
21
- Dir.entries(path)
22
- rescue Errno::ENOENT
23
- Logger::warning "App path does not exists"
24
- nil
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,44 +0,0 @@
1
- module SimpleBackup
2
- module Engine
3
- module AppStrategy
4
- class Capistrano < Abstract
5
- def backup(name, path, attr)
6
- shared = get_shared_path(path, attr)
7
- current = get_current_path(path, attr)
8
-
9
- paths = [current, shared].compact
10
-
11
- if paths.empty?
12
- Logger::warning "No capistrano paths for application"
13
- return false
14
- end
15
-
16
- @storage.backup do |dir|
17
- FileUtils.cp_r paths, dir
18
- end
19
-
20
- true
21
- end
22
-
23
- private
24
- def get_current_path(path, attr)
25
- current = Dir.new(File.join(path, attr[:current] || 'current') + '/')
26
- Logger::debug "Capistrano current path: #{current.path}"
27
- current
28
- rescue Errno::ENOENT
29
- Logger::warning "No capistrano current path for application"
30
- nil
31
- end
32
-
33
- def get_shared_path(path, attr)
34
- shared = Dir.new(File.join(path, attr[:shared] || 'shared'))
35
- Logger::debug "Capistrano shared path: #{shared.path}"
36
- shared
37
- rescue Errno::ENOENT
38
- Logger::warning "No capistrano shared path for application"
39
- nil
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,20 +0,0 @@
1
- require 'simple_backup/engine/app_strategy/abstract'
2
-
3
- module SimpleBackup
4
- module Engine
5
- module AppStrategy
6
- class Factory
7
- @@types = [:bare, :capistrano]
8
-
9
- def self.create(type)
10
- raise Exception::TypeDoesNotExists.new "Strategy type '#{type}' does not exists" unless @@types.include?(type)
11
- file = "simple_backup/engine/app_strategy/#{type.to_s}"
12
-
13
- require file
14
- strategy = Object.const_get("SimpleBackup::Engine::AppStrategy::#{type.to_s.capitalize}")
15
- strategy.new
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,63 +0,0 @@
1
- require 'simple_backup/engine/app_strategy/factory'
2
-
3
- module SimpleBackup
4
- module Engine
5
- class Apps < Abstract
6
- def initialize
7
- @apps = {}
8
- @strategies = {}
9
- end
10
-
11
- def app(path, attr = {})
12
- Logger::debug "Adding application #{path} #{attr}"
13
- raise Exception::AppAlreadyDefined.new "Application '#{path}' is already defined" if @apps.has_key?(path)
14
-
15
- @apps[path] = attr
16
- end
17
-
18
- def backup
19
- @apps.each do |path, attr|
20
- next unless app_exists(path)
21
- backup_app(path, attr)
22
- end
23
- end
24
-
25
- def sources
26
- sources = []
27
- @apps.each do |path, attr|
28
- sources << path
29
- end
30
-
31
- sources
32
- end
33
-
34
- private
35
- def backup_app(path, attr)
36
- name = path.split('/').last
37
- Logger::scope_start :info, "Backup of application #{name} started"
38
- Logger::debug "name: #{name}, attr: #{attr}"
39
-
40
- strategy = get_strategy(attr[:type])
41
- strategy.storage = @storage.space(name)
42
- is_backuped = strategy.backup(name, path, attr)
43
-
44
- Logger::scope_end :info, "Backup of application #{name} finished" if is_backuped
45
- Logger::scope_end :info, "Backup of application #{name} skipped" unless is_backuped
46
- end
47
-
48
- def app_exists(path)
49
- Dir.new(path)
50
- true
51
- rescue Errno::ENOENT
52
- Logger::warning "App path '#{path}' does not exists"
53
- false
54
- end
55
-
56
- def get_strategy(type)
57
- return @strategies[type] if @strategies.has_key?(type)
58
-
59
- AppStrategy::Factory.create(type)
60
- end
61
- end
62
- end
63
- end
@@ -1,104 +0,0 @@
1
- require 'mysql2'
2
-
3
- module SimpleBackup
4
- module Engine
5
- class MySQL < Abstract
6
- def initialize
7
- @host = 'localhost'
8
- @port = 3306
9
- @user = nil
10
- @pass = nil
11
- @dbs = {}
12
- end
13
-
14
- def host(host)
15
- @host = host
16
- end
17
-
18
- def port(port)
19
- @port = port
20
- end
21
-
22
- def user(user)
23
- @user = user
24
- end
25
-
26
- def pass(pass)
27
- @pass = pass
28
- end
29
-
30
- def db(name, attr = {})
31
- Logger::debug "Adding database #{name} #{attr}"
32
- raise Exception::AppAlreadyDefined.new "Database '#{name}' is already defined" if @dbs.has_key?(name)
33
-
34
- @dbs[name] = attr
35
- end
36
-
37
- def backup
38
- @conn = Mysql2::Client.new(host: @host, username: @user, password: @pass, port: @port)
39
-
40
- prepare_tables
41
- return if @dbs.empty?
42
-
43
- @storage.backup do |dir|
44
- @dbs.each do |db, attr|
45
- dump_db(dir, db, attr)
46
- end
47
- end
48
- ensure
49
- @conn.close unless @conn.nil?
50
- end
51
-
52
- def sources
53
- sources = []
54
- @dbs.each do |db, attr|
55
- sources << db
56
- end
57
-
58
- sources
59
- end
60
-
61
- private
62
- def prepare_tables
63
- @existing_dbs = []
64
- @conn.query("SHOW DATABASES").each do |row|
65
- @existing_dbs << row['Database']
66
- end
67
-
68
- dbs = {}
69
- @dbs.each do |db, attr|
70
- dbs[db] = attr if check_database_exists?(db)
71
- end
72
-
73
- @dbs = dbs
74
- @dbs.each do |db, attr|
75
- tables = []
76
- @conn.query("SHOW TABLES FROM `#{db}`").each do |row|
77
- tables << row["Tables_in_#{db}"]
78
- end
79
- tables = tables - attr[:exclude_tables] if attr[:exclude_tables]
80
-
81
- @dbs[db][:tables] ||= tables
82
- end
83
- end
84
-
85
- def check_database_exists?(db)
86
- if @existing_dbs.include?(db)
87
- return true
88
- end
89
- Logger::warning "Database '#{db}' does not exists"
90
- end
91
-
92
- def dump_db(dir, db, attr)
93
- Logger::scope_start :info, "Backup of mysql database #{db} started"
94
-
95
- file = File.join(dir, db) + '.sql'
96
- cmd = "mysqldump --flush-logs --flush-privileges --order-by-primary --complete-insert -C -h #{@host} -u #{@user} -p#{@pass} #{db} #{attr[:tables].join(' ')} > #{file}"
97
- Logger::debug "Running command: #{cmd}"
98
- `#{cmd}`
99
-
100
- Logger::scope_end :info, "Backup of mysql database #{db} finished"
101
- end
102
- end
103
- end
104
- end
@@ -1,6 +0,0 @@
1
- module SimpleBackup
2
- module Exception
3
- class AppAlreadyDefined < Base
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module SimpleBackup
2
- module Exception
3
- class AppsDirDoesNotExists < Base
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module SimpleBackup
2
- module Exception
3
- class Base < ::RuntimeError
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module SimpleBackup
2
- module Exception
3
- class CantCreateDir < Base
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module SimpleBackup
2
- module Exception
3
- class TypeDoesNotExists < Base
4
- end
5
- end
6
- end
@@ -1,5 +0,0 @@
1
- require 'simple_backup/exception/base'
2
- require 'simple_backup/exception/apps_dir_does_not_exists'
3
- require 'simple_backup/exception/app_already_defined'
4
- require 'simple_backup/exception/cant_create_dir'
5
- require 'simple_backup/exception/type_does_not_exists'
@@ -1,84 +0,0 @@
1
- require 'colorize'
2
-
3
- module SimpleBackup
4
- class Logger
5
- TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
6
-
7
- @@buffer = []
8
- @@scope = 0
9
- @@level = :info
10
- @@levels = {
11
- debug: {weight: 3, color: :light_cyan},
12
- info: {weight: 2, color: :green},
13
- warning: {weight: 1, color: :light_yellow},
14
- error: {weight: 0, color: :red}
15
- }
16
-
17
- banner = "LOG STARTED #{Time.new.strftime('%Y-%m-%dT%H:%M:%S')}"
18
- banner2 = "SimpleBackup v#{SimpleBackup::Version::get}"
19
-
20
- banner_length = 0
21
- banner_length = banner.length if banner.length > banner_length
22
- banner_length = banner2.length if banner2.length > banner_length
23
- banner_length = 80 if 80 > banner_length
24
-
25
- border = '=' * ((banner_length - banner.length) / 2).ceil.to_i
26
- @@buffer << "#{border}==[ #{banner} ]==#{border}"
27
- border = '=' * ((banner_length - banner2.length) / 2).ceil.to_i
28
- @@buffer << "#{border}==[ #{banner2} ]==#{border}"
29
- puts @@buffer[0].green
30
- puts @@buffer[1].green
31
-
32
- def self.level=(level)
33
- self.check_level(level)
34
- @@level = level
35
- end
36
-
37
- def self.scope_start(level = nil, message = nil)
38
- self.log level, message unless level.nil? and message.nil?
39
- @@scope += 1
40
- end
41
-
42
- def self.scope_end(level = nil, message = nil)
43
- self.log level, message unless level.nil? and message.nil?
44
- @@scope -= 1
45
- end
46
-
47
- def self.debug(message)
48
- self.log(:debug, message)
49
- end
50
-
51
- def self.info(message)
52
- self.log(:info, message)
53
- end
54
-
55
- def self.warning(message)
56
- self.log(:warning, message)
57
- end
58
-
59
- def self.error(message)
60
- self.log(:error, message)
61
- end
62
-
63
- def self.log(level, message)
64
- self.check_level(level)
65
-
66
- color = @@levels[level][:color]
67
- should_write = @@levels[level][:weight] <= @@levels[@@level][:weight]
68
-
69
- scope_prefix = '..' * @@scope
70
- message = "%s %7s: %s%s" % [Time.new.strftime(TIME_FORMAT), level.to_s.upcase, scope_prefix, message]
71
- @@buffer << message
72
-
73
- puts message.colorize(color: color) if should_write
74
- end
75
-
76
- def self.check_level(level)
77
- raise "Unknown logging level #{level}" unless @@levels.has_key?(level)
78
- end
79
-
80
- def self.buffer
81
- @@buffer
82
- end
83
- end
84
- end
@@ -1,138 +0,0 @@
1
- require 'mail'
2
- require 'socket'
3
-
4
- module SimpleBackup
5
- class Mailer
6
- def initialize(dsl, storage)
7
- @dsl = dsl
8
- @storage = storage
9
-
10
- @to = []
11
- @cc = []
12
- @bcc = []
13
- @hostname = Socket.gethostbyname(Socket.gethostname).first
14
- end
15
-
16
- def subject_prefix(prefix)
17
- @subject_prefix = prefix
18
- end
19
-
20
- def from(from)
21
- @from = from
22
- end
23
-
24
- def to(to)
25
- @to << to
26
- end
27
-
28
- def cc(cc)
29
- @cc << cc
30
- end
31
-
32
- def bcc(bcc)
33
- @bcc << bcc
34
- end
35
-
36
- def send
37
- Logger::scope_start :info, "Sending e-mail notification"
38
-
39
- Logger::info "Setting sender to: #{@from}"
40
- from = @from
41
- Logger::scope_start :info, "Adding recipients:"
42
- to = @to
43
- to.each do |mail|
44
- Logger::info "to: #{mail}"
45
- end
46
- cc = @cc
47
- cc.each do |mail|
48
- Logger::info "cc: #{mail}"
49
- end
50
- bcc = @bcc
51
- bcc.each do |mail|
52
- Logger::info "bcc: #{mail}"
53
- end
54
- Logger::scope_end
55
-
56
- @subject_prefix += '[FAILED]' if SimpleBackup.status == :failed
57
-
58
- subject = "%s Backup %s for %s" % [@subject_prefix, TIMESTAMP, @hostname]
59
- Logger::debug "Subject: #{subject}"
60
-
61
- body = get_body
62
-
63
- mail = Mail.new do
64
- from from
65
- to to
66
- cc cc
67
- bcc bcc
68
- subject subject.strip
69
- body body
70
- end
71
-
72
- mail.delivery_method :sendmail
73
- Logger::debug "Setting delivery method to sendmail"
74
-
75
- mail.deliver
76
- Logger::info "Notification sent"
77
-
78
- Logger::scope_end
79
- end
80
-
81
- private
82
- def get_body
83
- sources = ''
84
- @dsl.sources.each do |type, srcs|
85
- sources += "+ %s:\n" % type.to_s
86
- srcs.each do |src|
87
- sources += " - %s\n" % src
88
- end
89
- end
90
-
91
- backup_files = ''
92
- @storage.created_files.each do |file|
93
- backup_files += "- %s\n" % file
94
- end
95
-
96
- body = <<MAIL
97
- Hi,
98
-
99
- Backup #{TIMESTAMP} was created!
100
-
101
- Backup contains:
102
- #{sources}
103
- Created backup files:
104
- #{backup_files}
105
- Disk usage after backup:
106
- #{disk_usage}
107
- Backup log:
108
- ------------
109
- #{Logger::buffer.join("\n")}
110
- ------------
111
-
112
- Have a nice day,
113
- Backuper
114
-
115
- --
116
- Mail was send automatically
117
- Do not respond!
118
- MAIL
119
-
120
- body
121
- end
122
-
123
- def disk_usage
124
- content = "%16s %25s %12s %12s %12s %12s\n" % ['Mount', 'Filesystem', 'Size', 'Used', 'Available', 'Percent used']
125
-
126
- usage = Utils::Disk::usage
127
- usage[:mounts].each do |m|
128
- percent_usage = (m[:percent] * 100).to_s
129
- percent_usage = '(!!) ' + percent_usage if m[:high_usage_exceeded]
130
- content += "%16s %25s %8s MiB %8s MiB %8s MiB %11s%%\n" % [m[:mount], m[:fs], m[:size], m[:used], m[:available], percent_usage]
131
- end
132
-
133
- content += "\nHigh usage treshold exceeded!\nMax usage is #{usage[:high_usage]} where treshold is set to #{Utils::Disk::high_usage_treshold}\n" if usage[:high_usage_exceeded]
134
-
135
- content
136
- end
137
- end
138
- end
@@ -1,96 +0,0 @@
1
- require 'rubygems/package'
2
- require 'zlib'
3
- require 'fileutils'
4
-
5
- module SimpleBackup
6
- class Storage
7
- attr_accessor :dir
8
-
9
- @@created_files = []
10
-
11
- def dir=(dir)
12
- @dir = get_dir(dir)
13
-
14
- Logger::info "Backup dir set to '#{dir}'"
15
- end
16
-
17
- def space(space)
18
- Logger::debug "Setting backup_dir for space '#{space}'"
19
- storage = Storage.new
20
- storage.dir = File.join(@dir.path, format_for_path(space))
21
- storage
22
- end
23
-
24
- def backup
25
- dir = get_dir(File.join(@dir, TIMESTAMP))
26
- yield(dir)
27
-
28
- targz = targz(dir)
29
- backup_file = File.join(@dir, TIMESTAMP) + '.tar.gz'
30
-
31
- File.open(backup_file, 'w') do |f|
32
- f.write targz.string
33
- end
34
-
35
- FileUtils.rm_r dir.path
36
-
37
- @@created_files.push backup_file
38
- backup_file
39
- end
40
-
41
- def created_files
42
- @@created_files
43
- end
44
-
45
- private
46
- def get_dir(dir)
47
- tries ||= 2
48
- Dir.new(dir)
49
- rescue Errno::ENOENT
50
- recreate_dir(dir)
51
- retry unless (tries -= 1).zero?
52
- end
53
-
54
- def recreate_dir(dir)
55
- Dir.mkdir(dir, 0755)
56
-
57
- Logger::warning "Recreated non-existing directory '#{dir}'"
58
- rescue Errno::EACCES => e
59
- raise Exception::CantCreateDir.new(e.message)
60
- end
61
-
62
- def format_for_path(value)
63
- value.downcase.gsub(/[^a-zA-Z0-9\-\_\.]*/, '').gsub(/\s+/, '_')
64
- end
65
-
66
- def targz(dir)
67
- path = dir.path
68
- content = StringIO.new('');
69
- Gem::Package::TarWriter.new(content) do |tar|
70
- Dir[File.join(path, '**/*')].each do |file|
71
- mode = File.stat(file).mode
72
- relative_file = file.sub(/^#{Regexp::escape path}\/?/, '')
73
-
74
- if File.directory?(file)
75
- tar.mkdir(relative_file, mode)
76
- else
77
- tar.add_file relative_file, mode do |tf|
78
- File.open(file, 'rb') do |f|
79
- tf.write f.read
80
- end
81
- end
82
- end
83
- end
84
- end
85
-
86
- content.rewind
87
-
88
- gz = StringIO.new('')
89
- zip = Zlib::GzipWriter.new(gz)
90
- zip.write content.string
91
- zip.close
92
-
93
- gz
94
- end
95
- end
96
- end