backupii 0.1.0.pre.alpha.1
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.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +37 -0
- data/bin/backupii +5 -0
- data/bin/docker_test +24 -0
- data/lib/backup/archive.rb +171 -0
- data/lib/backup/binder.rb +23 -0
- data/lib/backup/cleaner.rb +114 -0
- data/lib/backup/cli.rb +376 -0
- data/lib/backup/cloud_io/base.rb +40 -0
- data/lib/backup/cloud_io/cloud_files.rb +301 -0
- data/lib/backup/cloud_io/s3.rb +256 -0
- data/lib/backup/compressor/base.rb +34 -0
- data/lib/backup/compressor/bzip2.rb +37 -0
- data/lib/backup/compressor/custom.rb +51 -0
- data/lib/backup/compressor/gzip.rb +76 -0
- data/lib/backup/config/dsl.rb +103 -0
- data/lib/backup/config/helpers.rb +139 -0
- data/lib/backup/config.rb +122 -0
- data/lib/backup/database/base.rb +89 -0
- data/lib/backup/database/mongodb.rb +189 -0
- data/lib/backup/database/mysql.rb +194 -0
- data/lib/backup/database/openldap.rb +97 -0
- data/lib/backup/database/postgresql.rb +134 -0
- data/lib/backup/database/redis.rb +179 -0
- data/lib/backup/database/riak.rb +82 -0
- data/lib/backup/database/sqlite.rb +57 -0
- data/lib/backup/encryptor/base.rb +29 -0
- data/lib/backup/encryptor/gpg.rb +745 -0
- data/lib/backup/encryptor/open_ssl.rb +76 -0
- data/lib/backup/errors.rb +55 -0
- data/lib/backup/logger/console.rb +50 -0
- data/lib/backup/logger/fog_adapter.rb +27 -0
- data/lib/backup/logger/logfile.rb +134 -0
- data/lib/backup/logger/syslog.rb +116 -0
- data/lib/backup/logger.rb +199 -0
- data/lib/backup/model.rb +478 -0
- data/lib/backup/notifier/base.rb +128 -0
- data/lib/backup/notifier/campfire.rb +63 -0
- data/lib/backup/notifier/command.rb +101 -0
- data/lib/backup/notifier/datadog.rb +107 -0
- data/lib/backup/notifier/flowdock.rb +101 -0
- data/lib/backup/notifier/hipchat.rb +118 -0
- data/lib/backup/notifier/http_post.rb +116 -0
- data/lib/backup/notifier/mail.rb +235 -0
- data/lib/backup/notifier/nagios.rb +67 -0
- data/lib/backup/notifier/pagerduty.rb +82 -0
- data/lib/backup/notifier/prowl.rb +70 -0
- data/lib/backup/notifier/pushover.rb +73 -0
- data/lib/backup/notifier/ses.rb +126 -0
- data/lib/backup/notifier/slack.rb +149 -0
- data/lib/backup/notifier/twitter.rb +57 -0
- data/lib/backup/notifier/zabbix.rb +62 -0
- data/lib/backup/package.rb +53 -0
- data/lib/backup/packager.rb +108 -0
- data/lib/backup/pipeline.rb +122 -0
- data/lib/backup/splitter.rb +75 -0
- data/lib/backup/storage/base.rb +72 -0
- data/lib/backup/storage/cloud_files.rb +158 -0
- data/lib/backup/storage/cycler.rb +73 -0
- data/lib/backup/storage/dropbox.rb +208 -0
- data/lib/backup/storage/ftp.rb +118 -0
- data/lib/backup/storage/local.rb +63 -0
- data/lib/backup/storage/qiniu.rb +68 -0
- data/lib/backup/storage/rsync.rb +251 -0
- data/lib/backup/storage/s3.rb +157 -0
- data/lib/backup/storage/scp.rb +67 -0
- data/lib/backup/storage/sftp.rb +82 -0
- data/lib/backup/syncer/base.rb +70 -0
- data/lib/backup/syncer/cloud/base.rb +180 -0
- data/lib/backup/syncer/cloud/cloud_files.rb +83 -0
- data/lib/backup/syncer/cloud/local_file.rb +99 -0
- data/lib/backup/syncer/cloud/s3.rb +118 -0
- data/lib/backup/syncer/rsync/base.rb +55 -0
- data/lib/backup/syncer/rsync/local.rb +29 -0
- data/lib/backup/syncer/rsync/pull.rb +49 -0
- data/lib/backup/syncer/rsync/push.rb +206 -0
- data/lib/backup/template.rb +45 -0
- data/lib/backup/utilities.rb +235 -0
- data/lib/backup/version.rb +5 -0
- data/lib/backup.rb +141 -0
- data/templates/cli/archive +28 -0
- data/templates/cli/compressor/bzip2 +4 -0
- data/templates/cli/compressor/custom +7 -0
- data/templates/cli/compressor/gzip +4 -0
- data/templates/cli/config +123 -0
- data/templates/cli/databases/mongodb +15 -0
- data/templates/cli/databases/mysql +18 -0
- data/templates/cli/databases/openldap +24 -0
- data/templates/cli/databases/postgresql +16 -0
- data/templates/cli/databases/redis +16 -0
- data/templates/cli/databases/riak +17 -0
- data/templates/cli/databases/sqlite +11 -0
- data/templates/cli/encryptor/gpg +27 -0
- data/templates/cli/encryptor/openssl +9 -0
- data/templates/cli/model +26 -0
- data/templates/cli/notifier/zabbix +15 -0
- data/templates/cli/notifiers/campfire +12 -0
- data/templates/cli/notifiers/command +32 -0
- data/templates/cli/notifiers/datadog +57 -0
- data/templates/cli/notifiers/flowdock +16 -0
- data/templates/cli/notifiers/hipchat +16 -0
- data/templates/cli/notifiers/http_post +32 -0
- data/templates/cli/notifiers/mail +24 -0
- data/templates/cli/notifiers/nagios +13 -0
- data/templates/cli/notifiers/pagerduty +12 -0
- data/templates/cli/notifiers/prowl +11 -0
- data/templates/cli/notifiers/pushover +11 -0
- data/templates/cli/notifiers/ses +15 -0
- data/templates/cli/notifiers/slack +22 -0
- data/templates/cli/notifiers/twitter +13 -0
- data/templates/cli/splitter +7 -0
- data/templates/cli/storages/cloud_files +11 -0
- data/templates/cli/storages/dropbox +20 -0
- data/templates/cli/storages/ftp +13 -0
- data/templates/cli/storages/local +8 -0
- data/templates/cli/storages/qiniu +12 -0
- data/templates/cli/storages/rsync +17 -0
- data/templates/cli/storages/s3 +16 -0
- data/templates/cli/storages/scp +15 -0
- data/templates/cli/storages/sftp +15 -0
- data/templates/cli/syncers/cloud_files +22 -0
- data/templates/cli/syncers/rsync_local +20 -0
- data/templates/cli/syncers/rsync_pull +28 -0
- data/templates/cli/syncers/rsync_push +28 -0
- data/templates/cli/syncers/s3 +27 -0
- data/templates/general/links +3 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +16 -0
- data/templates/notifier/mail/success.erb +16 -0
- data/templates/notifier/mail/warning.erb +16 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +507 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Syncer
|
5
|
+
module RSync
|
6
|
+
class Push < Base
|
7
|
+
##
|
8
|
+
# Mode of operation
|
9
|
+
#
|
10
|
+
# [:ssh (default)]
|
11
|
+
# Connects to the remote via SSH.
|
12
|
+
# Does not use an rsync daemon on the remote.
|
13
|
+
#
|
14
|
+
# [:ssh_daemon]
|
15
|
+
# Connects to the remote via SSH.
|
16
|
+
# Spawns a single-use daemon on the remote, which allows certain
|
17
|
+
# daemon features (like modules) to be used.
|
18
|
+
#
|
19
|
+
# [:rsync_daemon]
|
20
|
+
# Connects directly to an rsync daemon via TCP.
|
21
|
+
# Data transferred is not encrypted.
|
22
|
+
#
|
23
|
+
attr_accessor :mode
|
24
|
+
|
25
|
+
##
|
26
|
+
# Server Address
|
27
|
+
attr_accessor :host
|
28
|
+
|
29
|
+
##
|
30
|
+
# SSH or RSync port
|
31
|
+
#
|
32
|
+
# For `:ssh` or `:ssh_daemon` mode, this specifies the SSH port to use
|
33
|
+
# and defaults to 22.
|
34
|
+
#
|
35
|
+
# For `:rsync_daemon` mode, this specifies the TCP port to use
|
36
|
+
# and defaults to 873.
|
37
|
+
attr_accessor :port
|
38
|
+
|
39
|
+
##
|
40
|
+
# SSH User
|
41
|
+
#
|
42
|
+
# If the user running the backup is not the same user that needs to
|
43
|
+
# authenticate with the remote server, specify the user here.
|
44
|
+
#
|
45
|
+
# The user must have SSH keys setup for passphrase-less access to the
|
46
|
+
# remote. If the SSH User does not have passphrase-less keys, or no
|
47
|
+
# default keys in their `~/.ssh` directory, you will need to use the
|
48
|
+
# `-i` option in `:additional_ssh_options` to specify the
|
49
|
+
# passphrase-less key to use.
|
50
|
+
#
|
51
|
+
# Used only for `:ssh` and `:ssh_daemon` modes.
|
52
|
+
attr_accessor :ssh_user
|
53
|
+
|
54
|
+
##
|
55
|
+
# Additional SSH Options
|
56
|
+
#
|
57
|
+
# Used to supply a String or Array of options to be passed to the SSH
|
58
|
+
# command in `:ssh` and `:ssh_daemon` modes.
|
59
|
+
#
|
60
|
+
# For example, if you need to supply a specific SSH key for the
|
61
|
+
# `ssh_user`, you would set this to: "-i '/path/to/id_rsa'". Which would
|
62
|
+
# produce:
|
63
|
+
#
|
64
|
+
# rsync -e "ssh -p 22 -i '/path/to/id_rsa'"
|
65
|
+
#
|
66
|
+
# Arguments may be single-quoted, but should not contain any
|
67
|
+
# double-quotes.
|
68
|
+
#
|
69
|
+
# Used only for `:ssh` and `:ssh_daemon` modes.
|
70
|
+
attr_accessor :additional_ssh_options
|
71
|
+
|
72
|
+
##
|
73
|
+
# RSync User
|
74
|
+
#
|
75
|
+
# If the user running the backup is not the same user that needs to
|
76
|
+
# authenticate with the rsync daemon, specify the user here.
|
77
|
+
#
|
78
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
79
|
+
attr_accessor :rsync_user
|
80
|
+
|
81
|
+
##
|
82
|
+
# RSync Password
|
83
|
+
#
|
84
|
+
# If specified, Backup will write the password to a temporary file and
|
85
|
+
# use it with rsync's `--password-file` option for daemon
|
86
|
+
# authentication.
|
87
|
+
#
|
88
|
+
# Note that setting this will override `rsync_password_file`.
|
89
|
+
#
|
90
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
91
|
+
attr_accessor :rsync_password
|
92
|
+
|
93
|
+
##
|
94
|
+
# RSync Password File
|
95
|
+
#
|
96
|
+
# If specified, this path will be passed to rsync's `--password-file`
|
97
|
+
# option for daemon authentication.
|
98
|
+
#
|
99
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
100
|
+
attr_accessor :rsync_password_file
|
101
|
+
|
102
|
+
##
|
103
|
+
# Flag for compressing (only compresses for the transfer)
|
104
|
+
attr_accessor :compress
|
105
|
+
|
106
|
+
def initialize(syncer_id = nil)
|
107
|
+
super
|
108
|
+
|
109
|
+
@mode ||= :ssh
|
110
|
+
@port ||= mode == :rsync_daemon ? 873 : 22
|
111
|
+
@compress ||= false
|
112
|
+
end
|
113
|
+
|
114
|
+
def perform!
|
115
|
+
log!(:started)
|
116
|
+
write_password_file!
|
117
|
+
|
118
|
+
create_dest_path!
|
119
|
+
run("#{rsync_command} #{paths_to_push} " \
|
120
|
+
"#{host_options}'#{dest_path}'")
|
121
|
+
|
122
|
+
log!(:finished)
|
123
|
+
ensure
|
124
|
+
remove_password_file!
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
##
|
130
|
+
# Remove any preceeding '~/', since this is on the remote,
|
131
|
+
# and remove any trailing `/`.
|
132
|
+
def dest_path
|
133
|
+
@dest_path ||= path.sub(%r{^~/}, "").sub(%r{/$}, "")
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Runs a 'mkdir -p' command on the remote to ensure the dest_path
|
138
|
+
# exists. This used because rsync will attempt to create the path, but
|
139
|
+
# will only call 'mkdir' without the '-p' option. This is only
|
140
|
+
# applicable in :ssh mode, and only used if the path would require this.
|
141
|
+
def create_dest_path!
|
142
|
+
return unless mode == :ssh && dest_path.index("/").to_i > 0
|
143
|
+
|
144
|
+
run "#{utility(:ssh)} #{ssh_transport_args} #{host} " +
|
145
|
+
%("mkdir -p '#{dest_path}'")
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# For Push, this will prepend the #dest_path.
|
150
|
+
# For Pull, this will prepend the first path in #paths_to_pull.
|
151
|
+
def host_options
|
152
|
+
if mode == :ssh
|
153
|
+
"#{host}:"
|
154
|
+
else
|
155
|
+
user = "#{rsync_user}@" if rsync_user
|
156
|
+
"#{user}#{host}::"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Common base command, plus options for Push/Pull
|
162
|
+
def rsync_command
|
163
|
+
super << compress_option << password_option << transport_options
|
164
|
+
end
|
165
|
+
|
166
|
+
def compress_option
|
167
|
+
compress ? " --compress" : ""
|
168
|
+
end
|
169
|
+
|
170
|
+
def password_option
|
171
|
+
return "" if mode == :ssh
|
172
|
+
|
173
|
+
path = @password_file ? @password_file.path : rsync_password_file
|
174
|
+
path ? " --password-file='#{File.expand_path(path)}'" : ""
|
175
|
+
end
|
176
|
+
|
177
|
+
def transport_options
|
178
|
+
if mode == :rsync_daemon
|
179
|
+
" --port #{port}"
|
180
|
+
else
|
181
|
+
%( -e "#{utility(:ssh)} #{ssh_transport_args}")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def ssh_transport_args
|
186
|
+
args = "-p #{port} ".dup
|
187
|
+
args << "-l #{ssh_user} " if ssh_user
|
188
|
+
args << Array(additional_ssh_options).join(" ")
|
189
|
+
args.rstrip
|
190
|
+
end
|
191
|
+
|
192
|
+
def write_password_file!
|
193
|
+
return unless rsync_password && mode != :ssh
|
194
|
+
|
195
|
+
@password_file = Tempfile.new("backup-rsync-password")
|
196
|
+
@password_file.write(rsync_password)
|
197
|
+
@password_file.close
|
198
|
+
end
|
199
|
+
|
200
|
+
def remove_password_file!
|
201
|
+
@password_file.delete if @password_file
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module Backup
|
6
|
+
class Template
|
7
|
+
# Holds a binding object. Nil if not provided.
|
8
|
+
attr_accessor :binding
|
9
|
+
|
10
|
+
##
|
11
|
+
# Creates a new instance of the Backup::Template class and optionally takes
|
12
|
+
# an argument that can be either a binding object, a Hash or nil
|
13
|
+
def initialize(object = nil)
|
14
|
+
@binding =
|
15
|
+
if object.is_a?(Binding)
|
16
|
+
object
|
17
|
+
elsif object.is_a?(Hash)
|
18
|
+
Backup::Binder.new(object).get_binding
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Renders the provided file (in the context of the binding if any) to the
|
24
|
+
# console
|
25
|
+
def render(file)
|
26
|
+
puts result(file)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a String object containing the contents of the file (in the
|
31
|
+
# context of the binding if any)
|
32
|
+
def result(file)
|
33
|
+
ERB.new(file_contents(file), nil, "<>").result(binding)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
##
|
39
|
+
# Reads and returns the contents of the provided file path,
|
40
|
+
# relative from the Backup::TEMPLATE_PATH
|
41
|
+
def file_contents(file)
|
42
|
+
File.read(File.join(Backup::TEMPLATE_PATH, file))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Utilities
|
5
|
+
class Error < Backup::Error; end
|
6
|
+
|
7
|
+
UTILITIES_NAMES = %w[
|
8
|
+
tar cat split sudo chown hostname
|
9
|
+
gzip bzip2
|
10
|
+
mongo mongodump mysqldump innobackupex
|
11
|
+
pg_dump pg_dumpall redis-cli riak-admin
|
12
|
+
gpg openssl
|
13
|
+
rsync ssh
|
14
|
+
sendmail exim
|
15
|
+
send_nsca
|
16
|
+
zabbix_sender
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
class DSL
|
21
|
+
def initialize(utils)
|
22
|
+
@utilities = utils
|
23
|
+
end
|
24
|
+
|
25
|
+
# Helper methods to allow users to set the path for all utilities in the
|
26
|
+
# .configure block.
|
27
|
+
#
|
28
|
+
# Utility names with dashes (`redis-cli`) will be set using method calls
|
29
|
+
# with an underscore (`redis_cli`).
|
30
|
+
UTILITIES_NAMES.each do |util_name|
|
31
|
+
define_method util_name.tr("-", "_") do |raw_path|
|
32
|
+
path = File.expand_path(raw_path)
|
33
|
+
|
34
|
+
unless File.executable?(path)
|
35
|
+
raise Utilities::Error, <<-EOS
|
36
|
+
The path given for '#{util_name}' was not found or not executable.
|
37
|
+
Path was: #{path}
|
38
|
+
EOS
|
39
|
+
end
|
40
|
+
|
41
|
+
@utilities.utilities[util_name] = path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Allow users to set the +tar+ distribution if needed. (:gnu or :bsd)
|
46
|
+
def tar_dist(val)
|
47
|
+
Utilities.tar_dist(val)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
##
|
53
|
+
# Configure the path to system utilities used by Backup.
|
54
|
+
#
|
55
|
+
# Backup will attempt to locate any required system utilities using a
|
56
|
+
# +which+ command call. If a utility can not be found, or you need to
|
57
|
+
# specify an alternate path for a utility, you may do so in your
|
58
|
+
# +config.rb+ file using this method.
|
59
|
+
#
|
60
|
+
# Backup supports both GNU and BSD utilities.
|
61
|
+
# While Backup uses these utilities in a manner compatible with either
|
62
|
+
# version, the +tar+ utility requires some special handling with respect
|
63
|
+
# to +Archive+s. Backup will attempt to detect if the +tar+ command
|
64
|
+
# found (or set here) is GNU or BSD. If for some reason this fails,
|
65
|
+
# this may be set using the +tar_dist+ command shown below.
|
66
|
+
#
|
67
|
+
# Backup::Utilities.configure do
|
68
|
+
# # General Utilites
|
69
|
+
# tar '/path/to/tar'
|
70
|
+
# tar_dist :gnu # or :bsd
|
71
|
+
# cat '/path/to/cat'
|
72
|
+
# split '/path/to/split'
|
73
|
+
# sudo '/path/to/sudo'
|
74
|
+
# chown '/path/to/chown'
|
75
|
+
# hostname '/path/to/hostname'
|
76
|
+
#
|
77
|
+
# # Compressors
|
78
|
+
# gzip '/path/to/gzip'
|
79
|
+
# bzip2 '/path/to/bzip2'
|
80
|
+
#
|
81
|
+
# # Database Utilities
|
82
|
+
# mongo '/path/to/mongo'
|
83
|
+
# mongodump '/path/to/mongodump'
|
84
|
+
# mysqldump '/path/to/mysqldump'
|
85
|
+
# pg_dump '/path/to/pg_dump'
|
86
|
+
# pg_dumpall '/path/to/pg_dumpall'
|
87
|
+
# redis_cli '/path/to/redis-cli'
|
88
|
+
# riak_admin '/path/to/riak-admin'
|
89
|
+
#
|
90
|
+
# # Encryptors
|
91
|
+
# gpg '/path/to/gpg'
|
92
|
+
# openssl '/path/to/openssl'
|
93
|
+
#
|
94
|
+
# # Syncer and Storage
|
95
|
+
# rsync '/path/to/rsync'
|
96
|
+
# ssh '/path/to/ssh'
|
97
|
+
#
|
98
|
+
# # Notifiers
|
99
|
+
# sendmail '/path/to/sendmail'
|
100
|
+
# exim '/path/to/exim'
|
101
|
+
# send_nsca '/path/to/send_nsca'
|
102
|
+
# zabbix_sender '/path/to/zabbix_sender'
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# These paths may be set using absolute paths, or relative to the
|
106
|
+
# working directory when Backup is run.
|
107
|
+
def configure(&block)
|
108
|
+
DSL.new(self).instance_eval(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def tar_dist(val)
|
112
|
+
# the acceptance tests need to be able to reset this to nil
|
113
|
+
@gnu_tar = val.nil? ? nil : val == :gnu
|
114
|
+
end
|
115
|
+
|
116
|
+
def gnu_tar?
|
117
|
+
return @gnu_tar unless @gnu_tar.nil?
|
118
|
+
|
119
|
+
@gnu_tar = !!run("#{utility(:tar)} --version").match(%r{GNU})
|
120
|
+
end
|
121
|
+
|
122
|
+
def utilities
|
123
|
+
@utilities ||= {}
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
##
|
129
|
+
# Returns the full path to the specified utility.
|
130
|
+
# Raises an error if utility can not be found in the system's $PATH
|
131
|
+
def utility(name)
|
132
|
+
name = name.to_s.strip
|
133
|
+
raise Error, "Utility Name Empty" if name.empty?
|
134
|
+
|
135
|
+
utilities[name] ||= `which '#{name}' 2>/dev/null`.chomp
|
136
|
+
raise Error, <<-EOS if utilities[name].empty?
|
137
|
+
Could not locate '#{name}'.
|
138
|
+
Make sure the specified utility is installed
|
139
|
+
and available in your system's $PATH, or specify it's location
|
140
|
+
in your 'config.rb' file using Backup::Utilities.configure
|
141
|
+
EOS
|
142
|
+
|
143
|
+
utilities[name].dup
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Returns the name of the command name from the given command line.
|
148
|
+
# This is only used to simplify log messages.
|
149
|
+
def command_name(command)
|
150
|
+
parts = []
|
151
|
+
command = command.split(" ")
|
152
|
+
command.shift while command[0].to_s.include?("=")
|
153
|
+
parts << command.shift.split("/")[-1]
|
154
|
+
if parts[0] == "sudo"
|
155
|
+
until command.empty?
|
156
|
+
part = command.shift
|
157
|
+
if part.include?("/")
|
158
|
+
parts << part.split("/")[-1]
|
159
|
+
break
|
160
|
+
else
|
161
|
+
parts << part
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
parts.join(" ")
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Runs a system command
|
170
|
+
#
|
171
|
+
# All messages generated by the command will be logged.
|
172
|
+
# Messages on STDERR will be logged as warnings.
|
173
|
+
#
|
174
|
+
# If the command fails to execute, or returns a non-zero exit status
|
175
|
+
# an Error will be raised.
|
176
|
+
#
|
177
|
+
# Returns STDOUT
|
178
|
+
def run(command)
|
179
|
+
name = command_name(command)
|
180
|
+
Logger.info "Running system utility '#{name}'..."
|
181
|
+
|
182
|
+
begin
|
183
|
+
out = ""
|
184
|
+
err = ""
|
185
|
+
ps = Open4.popen4(command) do |_pid, stdin, stdout, stderr|
|
186
|
+
stdin.close
|
187
|
+
out = stdout.read.strip
|
188
|
+
err = stderr.read.strip
|
189
|
+
end
|
190
|
+
rescue Exception => err
|
191
|
+
raise Error.wrap(err, "Failed to execute '#{name}'")
|
192
|
+
end
|
193
|
+
|
194
|
+
unless ps.success?
|
195
|
+
raise Error, <<-EOS
|
196
|
+
'#{name}' failed with exit status: #{ps.exitstatus}
|
197
|
+
STDOUT Messages: #{out.empty? ? "None" : "\n#{out}"}
|
198
|
+
STDERR Messages: #{err.empty? ? "None" : "\n#{err}"}
|
199
|
+
EOS
|
200
|
+
end
|
201
|
+
|
202
|
+
unless out.empty?
|
203
|
+
Logger.info(out.lines.map { |line| "#{name}:STDOUT: #{line}" }.join)
|
204
|
+
end
|
205
|
+
|
206
|
+
unless err.empty?
|
207
|
+
Logger.warn(err.lines.map { |line| "#{name}:STDERR: #{line}" }.join)
|
208
|
+
end
|
209
|
+
|
210
|
+
out
|
211
|
+
end
|
212
|
+
|
213
|
+
def reset!
|
214
|
+
utilities.clear
|
215
|
+
@gnu_tar = nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Allows these utility methods to be included in other classes,
|
220
|
+
# while allowing them to be stubbed in spec_helper for all specs.
|
221
|
+
module Helpers
|
222
|
+
private
|
223
|
+
|
224
|
+
[:utility, :command_name, :run].each do |name|
|
225
|
+
define_method name do |arg|
|
226
|
+
Utilities.send(name, arg)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def gnu_tar?
|
231
|
+
Utilities.gnu_tar?
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
data/lib/backup.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Load Ruby Core Libraries
|
4
|
+
require "time"
|
5
|
+
require "fileutils"
|
6
|
+
require "tempfile"
|
7
|
+
require "syslog"
|
8
|
+
require "yaml"
|
9
|
+
require "etc"
|
10
|
+
require "forwardable"
|
11
|
+
|
12
|
+
require "open4"
|
13
|
+
require "thor"
|
14
|
+
require "shellwords"
|
15
|
+
|
16
|
+
require "excon"
|
17
|
+
# Include response.inspect in error messages.
|
18
|
+
Excon.defaults[:debug_response] = true
|
19
|
+
# Excon should not retry failed requests. We handle that.
|
20
|
+
Excon.defaults[:middlewares].delete(Excon::Middleware::Idempotent)
|
21
|
+
|
22
|
+
##
|
23
|
+
# The Backup Ruby Gem
|
24
|
+
module Backup
|
25
|
+
##
|
26
|
+
# Backup's internal paths
|
27
|
+
LIBRARY_PATH = File.join(File.dirname(__FILE__), "backup")
|
28
|
+
STORAGE_PATH = File.join(LIBRARY_PATH, "storage")
|
29
|
+
SYNCER_PATH = File.join(LIBRARY_PATH, "syncer")
|
30
|
+
DATABASE_PATH = File.join(LIBRARY_PATH, "database")
|
31
|
+
COMPRESSOR_PATH = File.join(LIBRARY_PATH, "compressor")
|
32
|
+
ENCRYPTOR_PATH = File.join(LIBRARY_PATH, "encryptor")
|
33
|
+
NOTIFIER_PATH = File.join(LIBRARY_PATH, "notifier")
|
34
|
+
TEMPLATE_PATH = File.expand_path("../templates", __dir__)
|
35
|
+
|
36
|
+
##
|
37
|
+
# Autoload Backup storage files
|
38
|
+
module Storage
|
39
|
+
autoload :Base, File.join(STORAGE_PATH, "base")
|
40
|
+
autoload :Cycler, File.join(STORAGE_PATH, "cycler")
|
41
|
+
autoload :S3, File.join(STORAGE_PATH, "s3")
|
42
|
+
autoload :CloudFiles, File.join(STORAGE_PATH, "cloud_files")
|
43
|
+
autoload :Ninefold, File.join(STORAGE_PATH, "ninefold")
|
44
|
+
autoload :Dropbox, File.join(STORAGE_PATH, "dropbox")
|
45
|
+
autoload :FTP, File.join(STORAGE_PATH, "ftp")
|
46
|
+
autoload :SFTP, File.join(STORAGE_PATH, "sftp")
|
47
|
+
autoload :SCP, File.join(STORAGE_PATH, "scp")
|
48
|
+
autoload :RSync, File.join(STORAGE_PATH, "rsync")
|
49
|
+
autoload :Local, File.join(STORAGE_PATH, "local")
|
50
|
+
autoload :Qiniu, File.join(STORAGE_PATH, "qiniu")
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Autoload Backup syncer files
|
55
|
+
module Syncer
|
56
|
+
autoload :Base, File.join(SYNCER_PATH, "base")
|
57
|
+
module Cloud
|
58
|
+
autoload :Base, File.join(SYNCER_PATH, "cloud", "base")
|
59
|
+
autoload :LocalFile, File.join(SYNCER_PATH, "cloud", "local_file")
|
60
|
+
autoload :CloudFiles, File.join(SYNCER_PATH, "cloud", "cloud_files")
|
61
|
+
autoload :S3, File.join(SYNCER_PATH, "cloud", "s3")
|
62
|
+
end
|
63
|
+
module RSync
|
64
|
+
autoload :Base, File.join(SYNCER_PATH, "rsync", "base")
|
65
|
+
autoload :Local, File.join(SYNCER_PATH, "rsync", "local")
|
66
|
+
autoload :Push, File.join(SYNCER_PATH, "rsync", "push")
|
67
|
+
autoload :Pull, File.join(SYNCER_PATH, "rsync", "pull")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Autoload Backup database files
|
73
|
+
module Database
|
74
|
+
autoload :Base, File.join(DATABASE_PATH, "base")
|
75
|
+
autoload :MySQL, File.join(DATABASE_PATH, "mysql")
|
76
|
+
autoload :PostgreSQL, File.join(DATABASE_PATH, "postgresql")
|
77
|
+
autoload :MongoDB, File.join(DATABASE_PATH, "mongodb")
|
78
|
+
autoload :Redis, File.join(DATABASE_PATH, "redis")
|
79
|
+
autoload :Riak, File.join(DATABASE_PATH, "riak")
|
80
|
+
autoload :OpenLDAP, File.join(DATABASE_PATH, "openldap")
|
81
|
+
autoload :SQLite, File.join(DATABASE_PATH, "sqlite")
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Autoload compressor files
|
86
|
+
module Compressor
|
87
|
+
autoload :Base, File.join(COMPRESSOR_PATH, "base")
|
88
|
+
autoload :Gzip, File.join(COMPRESSOR_PATH, "gzip")
|
89
|
+
autoload :Bzip2, File.join(COMPRESSOR_PATH, "bzip2")
|
90
|
+
autoload :Custom, File.join(COMPRESSOR_PATH, "custom")
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Autoload encryptor files
|
95
|
+
module Encryptor
|
96
|
+
autoload :Base, File.join(ENCRYPTOR_PATH, "base")
|
97
|
+
autoload :OpenSSL, File.join(ENCRYPTOR_PATH, "open_ssl")
|
98
|
+
autoload :GPG, File.join(ENCRYPTOR_PATH, "gpg")
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Autoload notification files
|
103
|
+
module Notifier
|
104
|
+
autoload :Base, File.join(NOTIFIER_PATH, "base")
|
105
|
+
autoload :Mail, File.join(NOTIFIER_PATH, "mail")
|
106
|
+
autoload :Twitter, File.join(NOTIFIER_PATH, "twitter")
|
107
|
+
autoload :Campfire, File.join(NOTIFIER_PATH, "campfire")
|
108
|
+
autoload :Prowl, File.join(NOTIFIER_PATH, "prowl")
|
109
|
+
autoload :Hipchat, File.join(NOTIFIER_PATH, "hipchat")
|
110
|
+
autoload :PagerDuty, File.join(NOTIFIER_PATH, "pagerduty")
|
111
|
+
autoload :Pushover, File.join(NOTIFIER_PATH, "pushover")
|
112
|
+
autoload :Slack, File.join(NOTIFIER_PATH, "slack")
|
113
|
+
autoload :HttpPost, File.join(NOTIFIER_PATH, "http_post")
|
114
|
+
autoload :Nagios, File.join(NOTIFIER_PATH, "nagios")
|
115
|
+
autoload :FlowDock, File.join(NOTIFIER_PATH, "flowdock")
|
116
|
+
autoload :Zabbix, File.join(NOTIFIER_PATH, "zabbix")
|
117
|
+
autoload :DataDog, File.join(NOTIFIER_PATH, "datadog")
|
118
|
+
autoload :Ses, File.join(NOTIFIER_PATH, "ses")
|
119
|
+
autoload :Command, File.join(NOTIFIER_PATH, "command")
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Require Backup base files
|
124
|
+
%w[
|
125
|
+
errors
|
126
|
+
logger
|
127
|
+
utilities
|
128
|
+
archive
|
129
|
+
binder
|
130
|
+
cleaner
|
131
|
+
model
|
132
|
+
config
|
133
|
+
cli
|
134
|
+
package
|
135
|
+
packager
|
136
|
+
pipeline
|
137
|
+
splitter
|
138
|
+
template
|
139
|
+
version
|
140
|
+
].each { |lib| require File.join(LIBRARY_PATH, lib) }
|
141
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
##
|
2
|
+
# Archive [Archive]
|
3
|
+
#
|
4
|
+
# Adding a file or directory (including sub-directories):
|
5
|
+
# archive.add "/path/to/a/file.rb"
|
6
|
+
# archive.add "/path/to/a/directory/"
|
7
|
+
#
|
8
|
+
# Excluding a file or directory (including sub-directories):
|
9
|
+
# archive.exclude "/path/to/an/excluded_file.rb"
|
10
|
+
# archive.exclude "/path/to/an/excluded_directory
|
11
|
+
#
|
12
|
+
# By default, relative paths will be relative to the directory
|
13
|
+
# where `backup perform` is executed, and they will be expanded
|
14
|
+
# to the root of the filesystem when added to the archive.
|
15
|
+
#
|
16
|
+
# If a `root` path is set, relative paths will be relative to the
|
17
|
+
# given `root` path and will not be expanded when added to the archive.
|
18
|
+
#
|
19
|
+
# archive.root '/path/to/archive/root'
|
20
|
+
#
|
21
|
+
archive :my_archive do |archive|
|
22
|
+
# Run the `tar` command using `sudo`
|
23
|
+
# archive.use_sudo
|
24
|
+
archive.add "/path/to/a/file.rb"
|
25
|
+
archive.add "/path/to/a/folder/"
|
26
|
+
archive.exclude "/path/to/a/excluded_file.rb"
|
27
|
+
archive.exclude "/path/to/a/excluded_folder"
|
28
|
+
end
|