outback 0.0.14 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +6 -0
- data/LICENSE +21 -0
- data/README.md +29 -3
- data/lib/outback.rb +16 -11
- data/lib/outback/archive.rb +6 -17
- data/lib/outback/backup.rb +24 -19
- data/lib/outback/cli.rb +6 -2
- data/lib/outback/configuration.rb +15 -10
- data/lib/outback/directory_source.rb +8 -7
- data/lib/outback/directory_target.rb +18 -11
- data/lib/outback/encryption_processor.rb +34 -0
- data/lib/outback/errors.rb +7 -0
- data/lib/outback/logging.rb +7 -0
- data/lib/outback/mysql_source.rb +9 -9
- data/lib/outback/processor.rb +17 -0
- data/lib/outback/s3_target.rb +18 -9
- data/lib/outback/sftp_target.rb +70 -0
- data/lib/outback/source.rb +9 -2
- data/lib/outback/source_archive.rb +17 -0
- data/lib/outback/support/attr_setter.rb +1 -1
- data/lib/outback/support/configurable.rb +5 -3
- data/lib/outback/target.rb +51 -14
- data/lib/outback/target_archive.rb +30 -0
- data/lib/outback/version.rb +3 -0
- data/lib/vendor/enumerable_ext.rb +9 -0
- data/lib/{outback/vendor → vendor}/metaclass.rb +1 -1
- data/lib/{outback/vendor → vendor}/methodphitamine.rb +1 -1
- data/lib/vendor/mysql.rb +1093 -0
- data/lib/vendor/mysql/charset.rb +325 -0
- data/lib/vendor/mysql/constants.rb +165 -0
- data/lib/vendor/mysql/error.rb +989 -0
- data/lib/vendor/mysql/packet.rb +78 -0
- data/lib/vendor/mysql/protocol.rb +770 -0
- data/lib/vendor/numeric_ext.rb +49 -0
- data/lib/vendor/string_ext.rb +19 -0
- metadata +53 -39
- data/MIT-LICENSE +0 -20
- data/VERSION +0 -1
- data/lib/outback/configuration_error.rb +0 -4
- data/lib/outback/directory_archive.rb +0 -8
- data/lib/outback/local_archive.rb +0 -6
- data/lib/outback/s3_archive.rb +0 -18
- data/lib/outback/temp_archive.rb +0 -5
- data/lib/outback/vendor/mysql.rb +0 -1214
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2a87a0171b3d8506d69d5d8b0611916a7bccd8a
|
4
|
+
data.tar.gz: d86ec7bddb738e5b9574073e06aae4ffa65b25c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f252a3dab0597a9578c62c94ae4a9b739068c7090d357861ec534db8ae83f2327b6b5eead6cdc7e45e14f3fd502397cb6747d1ac7f900aba4692087d8004921d
|
7
|
+
data.tar.gz: 3e5a5ef024af75c835dbb4fae15f6b9e0d193c2e4865223beac15a33f83bef2b1c5345ff8a2795fc6b81634594d447ec294aedb04ae7a2b05dd2dd835140dd47
|
data/CHANGELOG
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 onrooby
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ instantiated during a single run.
|
|
28
28
|
|
29
29
|
```` ruby
|
30
30
|
Outback::Configuration.new 'name' do
|
31
|
-
source :directory, '/
|
31
|
+
source :directory, '/var/www' do
|
32
32
|
exclude '/var/www/foo'
|
33
33
|
exclude '/var/www/icons/*.png'
|
34
34
|
end
|
@@ -45,6 +45,13 @@ Outback::Configuration.new 'name' do
|
|
45
45
|
# database 'specific_database'
|
46
46
|
end
|
47
47
|
|
48
|
+
processor :encryption do
|
49
|
+
password 'very secure encryption password'
|
50
|
+
|
51
|
+
# The default cipher is aes-256-cbc
|
52
|
+
#cipher 'openssl supported cipher'
|
53
|
+
end
|
54
|
+
|
48
55
|
# Amazon S3 storage
|
49
56
|
target :s3 do
|
50
57
|
access_key 'S3 access key'
|
@@ -56,6 +63,19 @@ Outback::Configuration.new 'name' do
|
|
56
63
|
# Just omit the definition to keep archives forever.
|
57
64
|
ttl 1.month
|
58
65
|
end
|
66
|
+
|
67
|
+
# SFTP storage
|
68
|
+
target :sftp, 'example.com' do
|
69
|
+
user 'backupuser'
|
70
|
+
# Usually, SSH keys will be used to connect to the server.
|
71
|
+
# Alternatively, a password can be given here.
|
72
|
+
#password 'your password'
|
73
|
+
|
74
|
+
# Default port is 22
|
75
|
+
# port 1234
|
76
|
+
ttl 1.day
|
77
|
+
path '/backups'
|
78
|
+
end
|
59
79
|
|
60
80
|
# Store on a local filesystem path
|
61
81
|
target :directory, '/media/backups/daily' do
|
@@ -79,7 +99,7 @@ Default configurations and commandline options
|
|
79
99
|
If you place your backup configurations in the file `/etc/outback.conf` they
|
80
100
|
will be read automatically when the outback executable is invoked. Make
|
81
101
|
sure to have correct permissions on the configuration files, as they might
|
82
|
-
include database passwords.
|
102
|
+
include database and encryption passwords.
|
83
103
|
|
84
104
|
Alternatively, you can pass in the configuration file to read as a
|
85
105
|
commandline argument. The default configuration file in /etc will then be
|
@@ -131,4 +151,10 @@ Other commandline options are:
|
|
131
151
|
* `-t` `--test` test configuration, then exit
|
132
152
|
* `-l` `--list` list available configurations, then exit
|
133
153
|
* `-h` `--help` display help
|
134
|
-
* `--version` display version
|
154
|
+
* `--version` display version
|
155
|
+
|
156
|
+
Known Issues
|
157
|
+
------------
|
158
|
+
|
159
|
+
The s3 gem currently doesn't support V4 signatures, which are required for
|
160
|
+
s3 regions introduced after January 2014.
|
data/lib/outback.rb
CHANGED
@@ -3,35 +3,40 @@ require 'fileutils'
|
|
3
3
|
require 'tempfile'
|
4
4
|
require 'tmpdir'
|
5
5
|
|
6
|
-
require 'active_support/core_ext'
|
7
|
-
|
8
6
|
require 's3'
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
require_relative 'vendor/string_ext'
|
9
|
+
require_relative 'vendor/numeric_ext'
|
10
|
+
require_relative 'vendor/enumerable_ext'
|
11
|
+
require_relative 'vendor/mysql'
|
12
|
+
require_relative 'vendor/metaclass'
|
13
|
+
require_relative 'vendor/methodphitamine'
|
13
14
|
|
15
|
+
require 'outback/version'
|
14
16
|
require 'outback/support/returning'
|
15
17
|
require 'outback/support/attr_setter'
|
16
18
|
require 'outback/support/configurable'
|
17
19
|
require 'outback/support/mysql_ext'
|
18
20
|
require 'outback/support/pathname_ext'
|
21
|
+
require 'outback/errors'
|
22
|
+
require 'outback/logging'
|
19
23
|
require 'outback/configuration'
|
20
|
-
require 'outback/
|
24
|
+
require 'outback/archive'
|
21
25
|
require 'outback/source'
|
26
|
+
require 'outback/source_archive'
|
22
27
|
require 'outback/directory_source'
|
23
28
|
require 'outback/mysql_source'
|
24
|
-
require 'outback/
|
25
|
-
require 'outback/
|
29
|
+
require 'outback/processor'
|
30
|
+
require 'outback/encryption_processor'
|
26
31
|
require 'outback/target'
|
32
|
+
require 'outback/target_archive'
|
27
33
|
require 'outback/directory_target'
|
28
|
-
require 'outback/directory_archive'
|
29
34
|
require 'outback/s3_target'
|
30
|
-
require 'outback/
|
35
|
+
require 'outback/sftp_target'
|
31
36
|
require 'outback/backup'
|
32
37
|
|
33
38
|
module Outback
|
34
|
-
|
39
|
+
TIME_FORMAT = '%Y%m%d%H%M%S'.freeze
|
35
40
|
|
36
41
|
class << self
|
37
42
|
%w(verbose silent).each do |method|
|
data/lib/outback/archive.rb
CHANGED
@@ -1,30 +1,19 @@
|
|
1
1
|
module Outback
|
2
2
|
class Archive
|
3
|
-
NAME_PATTERN =
|
3
|
+
NAME_PATTERN = /\A([A-Za-z0-9.\-]+)_(\d{14})_(\w+)/
|
4
4
|
|
5
|
-
attr_reader :filename, :backup_name, :timestamp, :source_name, :
|
5
|
+
attr_reader :filename, :backup_name, :timestamp, :source_name, :size
|
6
6
|
|
7
|
-
def initialize(filename
|
8
|
-
@filename
|
7
|
+
def initialize(filename)
|
8
|
+
@filename = Pathname.new(filename)
|
9
9
|
unless match_data = @filename.basename.to_s.match(NAME_PATTERN)
|
10
10
|
raise ArgumentError, 'invalid name'
|
11
11
|
end
|
12
12
|
@backup_name, @timestamp, @source_name = match_data.captures[0..2]
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
filename
|
15
|
+
def to_s
|
16
|
+
"#{filename}"
|
17
17
|
end
|
18
|
-
|
19
|
-
def open
|
20
|
-
filename.open
|
21
|
-
end
|
22
|
-
|
23
|
-
def outdated?
|
24
|
-
if timestamp && parent && parent.ttl
|
25
|
-
Time.now - Time.parse(timestamp) > parent.ttl
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
18
|
end
|
30
19
|
end
|
data/lib/outback/backup.rb
CHANGED
@@ -1,44 +1,49 @@
|
|
1
1
|
module Outback
|
2
2
|
class Backup
|
3
|
-
|
4
|
-
delegate :sources, :targets, :to => :configuration
|
3
|
+
include Logging
|
5
4
|
|
5
|
+
attr_reader :configuration, :name, :timestamp, :tmpdir
|
6
|
+
|
6
7
|
def initialize(configuration)
|
7
8
|
raise ArgumentError, "configuration required" unless configuration.is_a?(Outback::Configuration)
|
8
9
|
@configuration = configuration
|
9
10
|
@name = configuration.name
|
10
|
-
@timestamp = Time.now.
|
11
|
+
@timestamp = Time.now.strftime(Outback::TIME_FORMAT)
|
11
12
|
end
|
12
13
|
|
13
14
|
def run!
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
ensure
|
22
|
-
FileUtils.remove_entry_secure(tmpdir)
|
23
|
-
end
|
15
|
+
logger.info "Using working directory #{configuration.tmpdir}" if configuration.tmpdir
|
16
|
+
@tmpdir = Dir.mktmpdir([name, timestamp], configuration.tmpdir)
|
17
|
+
archives = create_archives
|
18
|
+
logger.info "Created #{archives.size} archives"
|
19
|
+
archives = process_archives(archives)
|
20
|
+
logger.info "Processed #{archives.size} archives"
|
21
|
+
store_archives(archives)
|
24
22
|
purge_targets
|
23
|
+
ensure
|
24
|
+
FileUtils.remove_entry_secure(tmpdir)
|
25
|
+
end
|
26
|
+
|
27
|
+
[:sources, :processors, :targets].each do |collection|
|
28
|
+
define_method(collection) { configuration.public_send(collection) }
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
28
32
|
|
29
33
|
def create_archives
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
sources.map { |source| source.create_archives(timestamp, tmpdir) }.flatten.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_archives(archives)
|
38
|
+
processors.inject(archives) { |archives, processor| processor.process!(archives) }.flatten.compact
|
34
39
|
end
|
35
40
|
|
36
|
-
def store_archives
|
41
|
+
def store_archives(archives)
|
37
42
|
targets.each { |target| target.put(archives) }
|
38
43
|
end
|
39
44
|
|
40
45
|
def purge_targets
|
41
|
-
targets.each { |target| target.purge!
|
46
|
+
targets.each { |target| target.purge! }
|
42
47
|
end
|
43
48
|
|
44
49
|
end
|
data/lib/outback/cli.rb
CHANGED
@@ -35,7 +35,11 @@ module Outback
|
|
35
35
|
end
|
36
36
|
option_parser.parse!
|
37
37
|
Outback::Configuration.reset
|
38
|
-
config_file = ARGV.first
|
38
|
+
config_file = if ARGV.first
|
39
|
+
ARGV.first.start_with?('/') ? ARGV.first : File.join(Dir.pwd, ARGV.first)
|
40
|
+
else
|
41
|
+
DEFAULT_CONFIGURATION_FILE
|
42
|
+
end
|
39
43
|
begin
|
40
44
|
load config_file
|
41
45
|
rescue ConfigurationError => conf_error
|
@@ -83,4 +87,4 @@ module Outback
|
|
83
87
|
end
|
84
88
|
|
85
89
|
end
|
86
|
-
end
|
90
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Outback
|
2
2
|
class Configuration
|
3
|
+
|
3
4
|
@loaded = []
|
4
5
|
|
5
6
|
class << self
|
6
7
|
def add(configuration)
|
7
|
-
raise ConfigurationError
|
8
|
+
raise ConfigurationError, "duplicate configuration #{configuration.name}" if loaded.any?(&its.name == configuration.name)
|
8
9
|
loaded << configuration
|
9
10
|
end
|
10
11
|
|
@@ -21,13 +22,13 @@ module Outback
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
attr_reader :name, :sources, :targets, :errors
|
25
|
+
attr_reader :name, :sources, :targets, :processors, :errors
|
25
26
|
|
26
27
|
def initialize(name, &block)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@sources, @targets, @errors = [], [], []
|
28
|
+
name = name.to_s
|
29
|
+
raise ConfigurationError, "Illegal configuration name #{name.inspect}" unless name.match(/\A[a-z][a-z0-9_\-.]+\z/)
|
30
|
+
@name = name
|
31
|
+
@sources, @processors, @targets, @errors = [], [], [], []
|
31
32
|
if block_given?
|
32
33
|
if block.arity == 1 then yield(self) else instance_eval(&block) end
|
33
34
|
end
|
@@ -48,21 +49,25 @@ module Outback
|
|
48
49
|
@tmpdir
|
49
50
|
elsif args.size == 1
|
50
51
|
dir = Pathname.new(args.first).realpath
|
51
|
-
raise
|
52
|
+
raise ConfigurationError, "tmpdir #{dir} is not a directory" unless dir.directory?
|
52
53
|
@tmpdir = dir
|
53
54
|
else
|
54
|
-
raise ConfigurationError
|
55
|
+
raise ConfigurationError, "tmpdir: wrong number of arguments(#{args.size} for 1)"
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
59
|
protected
|
59
60
|
|
60
61
|
def source(type, *args, &block)
|
61
|
-
"Outback::#{type.to_s.classify}Source".constantize.configure(*args, &block).tap { |instance| sources << instance }
|
62
|
+
"Outback::#{type.to_s.classify}Source".constantize.configure(name, *args, &block).tap { |instance| sources << instance }
|
63
|
+
end
|
64
|
+
|
65
|
+
def processor(type, *args, &block)
|
66
|
+
"Outback::#{type.to_s.classify}Processor".constantize.configure(name, *args, &block).tap { |instance| processors << instance }
|
62
67
|
end
|
63
68
|
|
64
69
|
def target(type, *args, &block)
|
65
|
-
"Outback::#{type.to_s.classify}Target".constantize.configure(*args, &block).tap { |instance| targets << instance }
|
70
|
+
"Outback::#{type.to_s.classify}Target".constantize.configure(name, *args, &block).tap { |instance| targets << instance }
|
66
71
|
end
|
67
72
|
|
68
73
|
def error(message)
|
@@ -2,7 +2,8 @@ module Outback
|
|
2
2
|
class DirectorySource < Source
|
3
3
|
attr_reader :path
|
4
4
|
|
5
|
-
def initialize(path)
|
5
|
+
def initialize(backup_name, path)
|
6
|
+
super(backup_name)
|
6
7
|
@path = path
|
7
8
|
end
|
8
9
|
|
@@ -18,18 +19,18 @@ module Outback
|
|
18
19
|
excludes.concat(paths.map(&:to_s)).uniq!
|
19
20
|
end
|
20
21
|
|
21
|
-
def create_archives(
|
22
|
+
def create_archives(timestamp, tmpdir)
|
22
23
|
source_dir = Pathname.new(path).realpath
|
23
24
|
archive_name = Pathname.new(tmpdir).join("#{backup_name}_#{timestamp}_#{source_name}.tar.gz")
|
24
25
|
exclude_list = Pathname.new(tmpdir).join('exclude_list.txt')
|
25
26
|
File.open(exclude_list, 'w') { |f| f << excludes.map { |e| e.to_s.sub(/\A\//, '') }.join("\n") }
|
26
27
|
verbose_switch = Outback.verbose? ? 'v' : ''
|
27
28
|
commandline = "tar -cz#{verbose_switch}pf #{archive_name} --exclude-from=#{exclude_list} --directory=/ #{source_dir.to_s.sub(/\A\//, '')}"
|
28
|
-
|
29
|
+
logger.debug "executing command: #{commandline}"
|
29
30
|
result = `#{commandline}`
|
30
|
-
|
31
|
-
|
32
|
-
[
|
31
|
+
logger.debug "result: #{result}"
|
32
|
+
logger.info "Archived directory #{path}"
|
33
|
+
[SourceArchive.new(archive_name)]
|
33
34
|
end
|
34
35
|
end
|
35
|
-
end
|
36
|
+
end
|
@@ -3,7 +3,8 @@ module Outback
|
|
3
3
|
attr_reader :path
|
4
4
|
attr_setter :user, :group, :directory_permissions, :archive_permissions, :ttl, :move
|
5
5
|
|
6
|
-
def initialize(path)
|
6
|
+
def initialize(backup_name, path)
|
7
|
+
super(backup_name)
|
7
8
|
@path = Pathname.new(path)
|
8
9
|
end
|
9
10
|
|
@@ -11,39 +12,45 @@ module Outback
|
|
11
12
|
(user and group) or (not user and not group)
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
+
def to_s
|
15
16
|
"directory:#{path}"
|
16
17
|
end
|
17
18
|
|
18
19
|
def put(archives)
|
19
|
-
|
20
|
+
FileUtils.mkdir_p(path) unless path.directory?
|
20
21
|
FileUtils.chmod directory_permissions || 0700, path
|
21
22
|
size = 0
|
22
23
|
archives.each do |archive|
|
23
24
|
basename = Pathname.new(archive.filename).basename
|
24
25
|
if move
|
25
|
-
|
26
|
+
logger.debug "moving #{archive.filename} to #{path}"
|
26
27
|
FileUtils.mv archive.filename, path
|
27
28
|
else
|
28
|
-
|
29
|
+
logger.debug "copying #{archive.filename} to #{path}"
|
29
30
|
FileUtils.cp_r archive.filename, path
|
30
31
|
end
|
31
32
|
archived_file = path.join(basename)
|
32
|
-
|
33
|
+
logger.debug "setting permissions for #{archived_file}"
|
33
34
|
FileUtils.chmod archive_permissions || 0600, archived_file
|
34
35
|
if user && group
|
35
|
-
|
36
|
+
logger.debug "setting owner #{user}, group #{group} for #{archived_file}"
|
36
37
|
FileUtils.chown user, group, archived_file
|
37
38
|
end
|
38
39
|
size += archived_file.size
|
39
40
|
end
|
40
|
-
|
41
|
+
logger.info "#{move ? 'Moved' : 'Copied'} #{archives.size} archives (#{size} bytes) to #{self}"
|
41
42
|
archives.size
|
42
43
|
end
|
44
|
+
|
45
|
+
private
|
43
46
|
|
44
|
-
def
|
45
|
-
path.files(Archive::NAME_PATTERN).map { |f|
|
47
|
+
def list_all_archives
|
48
|
+
path.files(Archive::NAME_PATTERN).map { |f| build_archive(f.to_s, f.size) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def unlink_archive!(archive)
|
52
|
+
archive.filename.unlink
|
46
53
|
end
|
47
54
|
|
48
55
|
end
|
49
|
-
end
|
56
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Outback
|
5
|
+
class EncryptionProcessor < Processor
|
6
|
+
|
7
|
+
attr_setter :password, :cipher
|
8
|
+
|
9
|
+
def cipher
|
10
|
+
@cipher ||= 'aes-256-cbc'
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"encryption:#{cipher}"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def process_archive!(archive)
|
20
|
+
result = nil
|
21
|
+
outfile = Pathname.new("#{archive.filename}.enc")
|
22
|
+
logger.debug "Encrypting #{archive} with #{self}"
|
23
|
+
Open3.popen3("openssl enc -#{cipher} -pass stdin -in #{archive.filename} -out #{outfile}") do |stdin, stdout, stderr, wait_thr|
|
24
|
+
stdin << password
|
25
|
+
stdin.close
|
26
|
+
result = wait_thr.value
|
27
|
+
end
|
28
|
+
raise ProcessingError, "error processing archive #{archive} in #{self}" unless result.success?
|
29
|
+
raise ProcessingError, "outfile #{outfile} not found" unless outfile.file?
|
30
|
+
archive.unlink
|
31
|
+
SourceArchive.new(outfile)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|