simple_backup 0.2.1 → 0.3.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.
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