backupper 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 433774005bc1b3d59b575acf5878d1bb9f202d5f
4
- data.tar.gz: 7efb646234fa7392394ce2980728961b13ae196c
2
+ SHA256:
3
+ metadata.gz: e342e000d1463ab2e5acda75380d51b4234e15c10bc9926de642657ffadbbe02
4
+ data.tar.gz: c5b08c11facbf564daaf375ccff26e0e8ba4aa59b5d03a0981dc8f69ea5d5463
5
5
  SHA512:
6
- metadata.gz: 0f97d4c32a27d19ab7925b6b868acda6325954cfa4870e3a795efd0a96c32930d2ee2258baa95df4fc0ee50aa5a88b9ab4d4d58ad2c9b36e44ed1137c456dbea
7
- data.tar.gz: 3439ec0927796ac074a51b51f68d32787c0992622e9dc9c46f38066f55f497bf59a1a15c257d910f5d1411465ecbd84814a530f6f71869cbbd17d6c5965ccaf3
6
+ metadata.gz: c2c543a27f6f781700acded008b3629a19507da9b10b3851cf513edceac10bcaf8e8efdb771dfee04ae2ee208ba966c5134b03e71002df6d65d3851cbc658a26
7
+ data.tar.gz: caccc5dd3ec837854f29efa9e94f11a02543d67c4f6e659f9a5d037760ffe3e5eed577d39eed86151d9e9fb69e5d7ca93d5aaf416adc58386b82dcc49e280269
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /.byebug_history
2
3
  /.yardoc
3
4
  /_yardoc/
4
5
  /coverage/
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in backupper.gemspec
6
6
  gemspec
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ backupper (0.4.0)
5
+ mail (~> 2.7)
6
+ sshkit (~> 1.15)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ byebug (11.0.1)
12
+ mail (2.7.1)
13
+ mini_mime (>= 0.1.1)
14
+ mini_mime (1.0.2)
15
+ net-scp (2.0.0)
16
+ net-ssh (>= 2.6.5, < 6.0.0)
17
+ net-ssh (5.2.0)
18
+ rake (10.5.0)
19
+ sshkit (1.20.0)
20
+ net-scp (>= 1.1.2)
21
+ net-ssh (>= 2.8.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+ x86_64-darwin-18
26
+
27
+ DEPENDENCIES
28
+ backupper!
29
+ bundler
30
+ byebug
31
+ rake (~> 10.0)
32
+
33
+ BUNDLED WITH
34
+ 2.0.2
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Backupper
2
2
 
3
- Backupper is a tool to backup all your databases spread in the world!
3
+ Backupper is a tool to backup all your databases spread all over the world!
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,7 +8,7 @@ Backupper is a tool to backup all your databases spread in the world!
8
8
 
9
9
  ## Usage
10
10
 
11
- $ backupper path/to/config.yml
11
+ $ backupper path/to/config.yml [options]
12
12
 
13
13
  ## Configuration file
14
14
 
@@ -52,6 +52,12 @@ After done all backups a report email is sent to you using gmail smtp service (r
52
52
 
53
53
  ⚠️ __WARNING__: the backupper configuration file contains many important passwords, so be careful to lock it and protect it with care!
54
54
 
55
+ To backup only specific databases you can pass the option `-o db1,db3`. In this way only databases under the key `db1` and `db3` will be backupped,
56
+ ignoring the others configuration keys.
57
+
58
+ ## Resources
59
+ * [Blog post about Backupper](https://tech.uqido.com/2018/07/16/backupper-tool-backup-database/) (italian only).
60
+
55
61
  ## Contributing
56
62
 
57
63
  Bug reports and pull requests are welcome on GitHub at https://github.com/uqido/backupper.
@@ -67,4 +73,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
67
73
  Backupper is maintained and funded by [Uqido](https://uqido.com).
68
74
  The names and logos for Uqido are trademarks of Uqido s.r.l.
69
75
 
70
- The [Uqido team](https://www.uqido.com/en/about-us/).
76
+ The [Uqido team](https://www.uqido.com/en/about-us/).
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -1,5 +1,4 @@
1
-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'backupper/version'
5
4
 
@@ -9,8 +8,8 @@ Gem::Specification.new do |spec|
9
8
  spec.authors = ['pioz']
10
9
  spec.email = ['enrico.pilotto@uqido.com']
11
10
 
12
- spec.summary = %q{Tool to backup databases}
13
- spec.description = %q{Backupper is a tool to backup all your databases spread in the world}
11
+ spec.summary = 'Tool to backup databases'
12
+ spec.description = 'Backupper is a tool to backup all your databases spread all over the world'
14
13
  spec.homepage = 'https://github.com/uqido/backupper'
15
14
  spec.license = 'MIT'
16
15
 
@@ -22,16 +21,17 @@ Gem::Specification.new do |spec|
22
21
  raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
23
22
  end
24
23
 
25
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
- f.match(%r{^(test|spec|features)/})
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(/^(test|spec|features)\//)
27
26
  end
28
- spec.bindir = "bin"
29
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
27
+ spec.bindir = 'bin'
28
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
31
30
 
32
- spec.add_runtime_dependency 'sshkit', '~> 1.15'
33
31
  spec.add_runtime_dependency 'mail', '~> 2.7'
32
+ spec.add_runtime_dependency 'sshkit', '~> 1.15'
34
33
 
35
- spec.add_development_dependency 'bundler', '~> 1.16'
34
+ spec.add_development_dependency 'bundler'
35
+ spec.add_development_dependency 'byebug'
36
36
  spec.add_development_dependency 'rake', '~> 10.0'
37
37
  end
@@ -1,11 +1,34 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'backupper'
3
+ require 'optparse'
4
4
 
5
- if ARGV.size != 1
6
- puts "usage: #{$0} path/to/config.yml"
5
+ require_relative '../lib/backupper'
6
+
7
+ options = {}
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "usage: #{$PROGRAM_NAME} path/to/config.yml [options]"
10
+
11
+ opts.on('-v', 'Print version') do |v|
12
+ options[:version] = v
13
+ end
14
+
15
+ opts.on('-o', '--only=conf-key1,conf-key2', Array, 'Backup only specific database configurations') do |keys|
16
+ options[:only] = keys
17
+ end
18
+ end
19
+
20
+ begin
21
+ parser.parse!
22
+ raise OptionParser::InvalidOption if ARGV.size != 1
23
+ rescue OptionParser::InvalidOption
24
+ puts parser.banner
7
25
  exit 1
8
26
  end
9
27
 
28
+ if options[:version]
29
+ puts Backupper::VERSION
30
+ exit 0
31
+ end
32
+
10
33
  b = Backupper.new(ARGV.first)
11
- b.backup!
34
+ b.backup!(options[:only])
@@ -1,2 +1,2 @@
1
- require 'backupper/backupper'
2
- require 'backupper/version'
1
+ require_relative 'backupper/backupper'
2
+ require_relative 'backupper/version'
@@ -1,17 +1,18 @@
1
- require 'yaml'
2
1
  require 'fileutils'
3
2
  require 'sshkit'
4
3
  require 'sshkit/dsl'
5
- require 'backupper/dump_command'
6
- require 'backupper/mailer'
7
- include SSHKit::DSL
4
+ require 'yaml'
5
+
6
+ require_relative 'dump_command'
7
+ require_relative 'mailer'
8
8
 
9
9
  class Backupper
10
+ include SSHKit::DSL
10
11
 
11
12
  SSHKit::Backend::Netssh.configure do |ssh|
12
13
  ssh.connection_timeout = 30
13
14
  ssh.ssh_options = {
14
- auth_methods: %w(publickey password)
15
+ auth_methods: %w[publickey password]
15
16
  }
16
17
  end
17
18
 
@@ -19,17 +20,20 @@ class Backupper
19
20
  conf = YAML.load_file(conf_file_path)
20
21
  @default = conf['default'] || {}
21
22
  @mailer = conf['mailer'] || {}
22
- @conf = conf.select{|k, v| !%w(default mailer).include?(k) && (v['disabled'].nil? || v['disabled'] == false)}
23
+ @conf = conf.select { |k, v| !%w[default mailer].include?(k) && (v['disabled'].nil? || v['disabled'] == false) }
23
24
  @report = {}
25
+ @logger = SSHKit::Formatter::Pretty.new($stdout)
24
26
  end
25
27
 
26
- def backup!
27
- @conf.each do |k, options|
28
- puts "⬇️ backing up #{k}..."
28
+ def backup!(only_these_keys = nil)
29
+ filtered_conf = @conf
30
+ filtered_conf = @conf.slice(*only_these_keys) if only_these_keys
31
+ filtered_conf.each do |k, options|
32
+ @logger.info "[#{Time.now}] ⬇️ backing up #{k}..."
29
33
  o, err = setup_options(options)
30
34
  if err
31
35
  error(k, err)
32
- puts err
36
+ @logger.error err
33
37
  next
34
38
  end
35
39
  begin
@@ -47,7 +51,7 @@ class Backupper
47
51
  )
48
52
  rescue SSHKit::Runner::ExecuteError => e
49
53
  error(k, e.to_s)
50
- puts e
54
+ @logger.error e
51
55
  end
52
56
  end
53
57
  send_report_email!
@@ -73,58 +77,55 @@ class Backupper
73
77
  size: (File.size(path).to_f / 2**20).round(2),
74
78
  time: (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t).round(2)
75
79
  }
76
- @report[key].merge!({extra_copy: File.absolute_path(File.join(extra_copy, dumpname))}) if extra_copy
80
+ @report[key].merge!({ extra_copy: File.absolute_path(File.join(extra_copy, dumpname)) }) if extra_copy
77
81
  end
78
82
 
79
83
  private
80
84
 
81
- def setup_options(options)
82
- o = @default.merge(options)
83
- o['outdir'] = check_dir(o['dump'].to_s)
84
- unless o['outdir']
85
- return nil, 'Invalid directory where to save database dump'
86
- end
87
- unless o['database']
88
- return nil, 'Please specify the database name!'
89
- end
90
- unless o['host']
91
- return nil, 'Please specify the host!'
92
- end
93
- o['url'] = o['host']
94
- o['url'] = "#{o['username']}@#{o['url']}" if o['username']
95
- o['url'] = "#{o['url']}:#{o['port']}" if o['port']
96
- o['adapter'] ||= 'mysql'
97
- unless DumpCommand.respond_to?(o['adapter'])
98
- return nil, "Cannot handle adapter '#{o['adapter']}'"
85
+ def setup_options(options)
86
+ o = @default.merge(options)
87
+ o['outdir'] = check_dir(o['dump'].to_s)
88
+ return nil, 'Invalid directory where to save database dump' unless o['outdir']
89
+ return nil, 'Please specify the database name!' unless o['database']
90
+ return nil, 'Please specify the host!' unless o['host']
91
+
92
+ o['url'] = o['host']
93
+ o['url'] = "#{o['username']}@#{o['url']}" if o['username']
94
+ o['url'] = "#{o['url']}:#{o['port']}" if o['port']
95
+ o['adapter'] ||= 'mysql'
96
+ unless DumpCommand.respond_to?(o['adapter'])
97
+ return nil, "Cannot handle adapter '#{o['adapter']}'"
98
+ end
99
+
100
+ return o, nil
99
101
  end
100
- return o, nil
101
- end
102
102
 
103
- def send_report_email!
104
- if @report.any? && @mailer['from'] && @mailer['to'] && @mailer['password']
105
- begin
106
- Mailer.send(from: @mailer['from'], to: @mailer['to'], password: @mailer['password'], report: @report)
107
- rescue Net::SMTPAuthenticationError => e
108
- puts e
103
+ def send_report_email!
104
+ if @report.any? && @mailer['from'] && @mailer['to'] && @mailer['password']
105
+ begin
106
+ Mailer.send(from: @mailer['from'], to: @mailer['to'], password: @mailer['password'], report: @report)
107
+ rescue Net::SMTPAuthenticationError => e
108
+ @logger.error e
109
+ end
109
110
  end
110
111
  end
111
- end
112
112
 
113
- def error(key, error)
114
- @report[key] = {error: error}
115
- end
113
+ def error(key, error)
114
+ @report[key] = { error: error }
115
+ end
116
116
 
117
- def check_dir(dirpath)
118
- return nil if dirpath.nil?
119
- unless File.exists?(dirpath)
120
- begin
121
- FileUtils::mkdir_p(dirpath)
122
- rescue => e
123
- return nil
117
+ def check_dir(dirpath)
118
+ return nil if dirpath.nil?
119
+
120
+ unless File.exist?(dirpath)
121
+ begin
122
+ FileUtils.mkdir_p(dirpath)
123
+ rescue StandardError => _e
124
+ return nil
125
+ end
124
126
  end
125
- end
126
- return File.dirname(dirpath) unless File.directory?(dirpath)
127
- return dirpath
128
- end
127
+ return File.dirname(dirpath) unless File.directory?(dirpath)
129
128
 
130
- end
129
+ return dirpath
130
+ end
131
+ end
@@ -1,5 +1,4 @@
1
1
  module DumpCommand
2
-
3
2
  def self.mysql(database:, username: 'root', password: nil, dump_options: nil, outfile:)
4
3
  params = []
5
4
  params << "--databases '#{database}'"
@@ -16,5 +15,4 @@ module DumpCommand
16
15
  params << dump_options if dump_options
17
16
  return "PGPASSWORD=#{password} pg_dump #{params.join(' ')} | bzip2 > '#{outfile}'"
18
17
  end
19
-
20
- end
18
+ end
@@ -1,7 +1,6 @@
1
1
  require 'mail'
2
2
 
3
3
  class Mailer
4
-
5
4
  def self.send(from:, to:, password:, report:)
6
5
  options = {
7
6
  address: 'smtp.gmail.com',
@@ -17,47 +16,47 @@ class Mailer
17
16
  Mail.deliver do
18
17
  to to
19
18
  from from
20
- subject Mailer.subject(report)
21
- body Mailer.body(report)
19
+ subject self.subject(report)
20
+ body self.body(report)
22
21
  end
23
22
  end
24
23
 
25
- private
24
+ class << self
25
+ private
26
26
 
27
- def self.body(report)
28
- b = []
29
- report.each do |k, data|
30
- s = ''
31
- if data[:error]
32
- s << "❌ #{k}\n"
33
- s << '=' * 80 << "\n"
34
- s << "Backup FAILED!\n"
35
- s << " error: #{data[:error]}\n"
36
- b << s
37
- else
38
- s << "️✅ #{k}\n"
39
- s << '=' * 80 << "\n"
40
- s << "Backup SUCCESS!\n"
41
- s << " dump size: #{data[:size]} MB\n"
42
- s << " time: #{data[:time]} seconds\n"
43
- s << " dump saved in: #{data[:path]}\n"
44
- if data[:extra_copy]
45
- s << " extra copy in: #{data[:extra_copy]}\n"
46
- else
47
- s << " no extra copy has been made\n"
27
+ def body(report)
28
+ b = []
29
+ report.each do |k, data|
30
+ s = ''
31
+ if data[:error]
32
+ s << "❌ #{k}\n"
33
+ s << '=' * 80 << "\n"
34
+ s << "Backup FAILED!\n"
35
+ s << " error: #{data[:error]}\n"
36
+ else
37
+ s << "️✅ #{k}\n"
38
+ s << '=' * 80 << "\n"
39
+ s << "Backup SUCCESS!\n"
40
+ s << " dump size: #{data[:size]} MB\n"
41
+ s << " time: #{data[:time]} seconds\n"
42
+ s << " dump saved in: #{data[:path]}\n"
43
+ if data[:extra_copy]
44
+ s << " extra copy in: #{data[:extra_copy]}\n"
45
+ else
46
+ s << " no extra copy has been made\n"
47
+ end
48
+ end
49
+ b << s
48
50
  end
49
- b << s
50
- end
51
- end
52
- return "Report for backups (#{Time.now})\n\n#{b.join("\n\n")}"
53
- end
51
+ return "Report for backups (#{Time.now})\n\n#{b.join("\n\n")}"
52
+ end
54
53
 
55
- def self.subject(report)
56
- errors = report.select{|k, v| v[:error]}.size
57
- icon = '✅'
58
- icon = '⚠️' if errors > 0
59
- icon = '❌' if errors == report.size
60
- return "[Backupper] #{report.size-errors}/#{report.size} backups successfully completed #{icon}"
54
+ def subject(report)
55
+ errors = report.select { |_k, v| v[:error] }.size
56
+ icon = '✅'
57
+ icon = '⚠️' if errors > 0
58
+ icon = '❌' if errors == report.size
59
+ return "[Backupper] #{report.size - errors}/#{report.size} backups successfully completed #{icon}"
60
+ end
61
61
  end
62
-
63
- end
62
+ end
@@ -1,3 +1,3 @@
1
1
  class Backupper
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backupper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pioz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-04 00:00:00.000000000 Z
11
+ date: 2019-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: sshkit
14
+ name: mail
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.15'
19
+ version: '2.7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.15'
26
+ version: '2.7'
27
27
  - !ruby/object:Gem::Dependency
28
- name: mail
28
+ name: sshkit
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.7'
33
+ version: '1.15'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.7'
40
+ version: '1.15'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.16'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '1.16'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +80,8 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '10.0'
69
- description: Backupper is a tool to backup all your databases spread in the world
83
+ description: Backupper is a tool to backup all your databases spread all over the
84
+ world
70
85
  email:
71
86
  - enrico.pilotto@uqido.com
72
87
  executables:
@@ -76,6 +91,7 @@ extra_rdoc_files: []
76
91
  files:
77
92
  - ".gitignore"
78
93
  - Gemfile
94
+ - Gemfile.lock
79
95
  - LICENSE.txt
80
96
  - README.md
81
97
  - Rakefile
@@ -106,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
122
  - !ruby/object:Gem::Version
107
123
  version: '0'
108
124
  requirements: []
109
- rubyforge_project:
110
- rubygems_version: 2.6.13
125
+ rubygems_version: 3.0.2
111
126
  signing_key:
112
127
  specification_version: 4
113
128
  summary: Tool to backup databases