backup 4.4.1 → 5.0.0.beta.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 +5 -5
- data/LICENSE +19 -0
- data/README.md +1 -1
- data/lib/backup.rb +74 -78
- data/lib/backup/archive.rb +31 -32
- data/lib/backup/binder.rb +2 -6
- data/lib/backup/cleaner.rb +14 -18
- data/lib/backup/cli.rb +104 -108
- data/lib/backup/cloud_io/base.rb +4 -7
- data/lib/backup/cloud_io/cloud_files.rb +60 -62
- data/lib/backup/cloud_io/s3.rb +69 -76
- data/lib/backup/compressor/base.rb +4 -7
- data/lib/backup/compressor/bzip2.rb +3 -7
- data/lib/backup/compressor/custom.rb +2 -6
- data/lib/backup/compressor/gzip.rb +16 -17
- data/lib/backup/config.rb +17 -18
- data/lib/backup/config/dsl.rb +16 -17
- data/lib/backup/config/helpers.rb +10 -16
- data/lib/backup/database/base.rb +22 -21
- data/lib/backup/database/mongodb.rb +36 -37
- data/lib/backup/database/mysql.rb +40 -41
- data/lib/backup/database/openldap.rb +8 -10
- data/lib/backup/database/postgresql.rb +29 -30
- data/lib/backup/database/redis.rb +27 -30
- data/lib/backup/database/riak.rb +15 -18
- data/lib/backup/database/sqlite.rb +4 -6
- data/lib/backup/encryptor/base.rb +2 -4
- data/lib/backup/encryptor/gpg.rb +49 -59
- data/lib/backup/encryptor/open_ssl.rb +11 -14
- data/lib/backup/errors.rb +7 -12
- data/lib/backup/logger.rb +16 -18
- data/lib/backup/logger/console.rb +5 -8
- data/lib/backup/logger/fog_adapter.rb +2 -6
- data/lib/backup/logger/logfile.rb +10 -12
- data/lib/backup/logger/syslog.rb +2 -4
- data/lib/backup/model.rb +75 -40
- data/lib/backup/notifier/base.rb +24 -26
- data/lib/backup/notifier/campfire.rb +9 -11
- data/lib/backup/notifier/command.rb +0 -3
- data/lib/backup/notifier/datadog.rb +9 -12
- data/lib/backup/notifier/flowdock.rb +13 -17
- data/lib/backup/notifier/hipchat.rb +11 -13
- data/lib/backup/notifier/http_post.rb +11 -14
- data/lib/backup/notifier/mail.rb +44 -47
- data/lib/backup/notifier/nagios.rb +5 -9
- data/lib/backup/notifier/pagerduty.rb +10 -12
- data/lib/backup/notifier/prowl.rb +15 -15
- data/lib/backup/notifier/pushover.rb +7 -10
- data/lib/backup/notifier/ses.rb +34 -16
- data/lib/backup/notifier/slack.rb +39 -40
- data/lib/backup/notifier/twitter.rb +2 -5
- data/lib/backup/notifier/zabbix.rb +11 -14
- data/lib/backup/package.rb +5 -9
- data/lib/backup/packager.rb +16 -17
- data/lib/backup/pipeline.rb +17 -21
- data/lib/backup/splitter.rb +8 -11
- data/lib/backup/storage/base.rb +5 -8
- data/lib/backup/storage/cloud_files.rb +21 -23
- data/lib/backup/storage/cycler.rb +10 -15
- data/lib/backup/storage/dropbox.rb +15 -21
- data/lib/backup/storage/ftp.rb +8 -10
- data/lib/backup/storage/local.rb +5 -8
- data/lib/backup/storage/qiniu.rb +8 -8
- data/lib/backup/storage/rsync.rb +24 -26
- data/lib/backup/storage/s3.rb +27 -28
- data/lib/backup/storage/scp.rb +10 -12
- data/lib/backup/storage/sftp.rb +10 -12
- data/lib/backup/syncer/base.rb +5 -8
- data/lib/backup/syncer/cloud/base.rb +27 -30
- data/lib/backup/syncer/cloud/cloud_files.rb +16 -18
- data/lib/backup/syncer/cloud/local_file.rb +5 -8
- data/lib/backup/syncer/cloud/s3.rb +23 -24
- data/lib/backup/syncer/rsync/base.rb +6 -10
- data/lib/backup/syncer/rsync/local.rb +1 -5
- data/lib/backup/syncer/rsync/pull.rb +6 -10
- data/lib/backup/syncer/rsync/push.rb +18 -22
- data/lib/backup/template.rb +9 -14
- data/lib/backup/utilities.rb +82 -69
- data/lib/backup/version.rb +1 -3
- metadata +100 -660
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Database
|
5
3
|
class OpenLDAP < Base
|
@@ -33,11 +31,11 @@ module Backup
|
|
33
31
|
super
|
34
32
|
instance_eval(&block) if block_given?
|
35
33
|
|
36
|
-
@name ||=
|
34
|
+
@name ||= "ldap_backup"
|
37
35
|
@use_sudo ||= false
|
38
|
-
@slapcat_args ||=
|
36
|
+
@slapcat_args ||= []
|
39
37
|
@slapcat_utility ||= utility(:slapcat)
|
40
|
-
@slapcat_conf ||=
|
38
|
+
@slapcat_conf ||= "/etc/ldap/slapd.d"
|
41
39
|
end
|
42
40
|
|
43
41
|
##
|
@@ -47,7 +45,7 @@ module Backup
|
|
47
45
|
super
|
48
46
|
|
49
47
|
pipeline = Pipeline.new
|
50
|
-
dump_ext =
|
48
|
+
dump_ext = "ldif"
|
51
49
|
|
52
50
|
pipeline << slapcat
|
53
51
|
if @model.compressor
|
@@ -57,8 +55,8 @@ module Backup
|
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
60
|
-
pipeline << "#{
|
61
|
-
"'#{
|
58
|
+
pipeline << "#{utility(:cat)} > " \
|
59
|
+
"'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
|
62
60
|
|
63
61
|
pipeline.run
|
64
62
|
if pipeline.success?
|
@@ -73,7 +71,7 @@ module Backup
|
|
73
71
|
##
|
74
72
|
# Builds the full slapcat string based on all attributes
|
75
73
|
def slapcat
|
76
|
-
command = "#{
|
74
|
+
command = "#{slapcat_utility} #{slapcat_conf_option} #{slapcat_conf} #{user_options}"
|
77
75
|
command.prepend("sudo ") if use_sudo
|
78
76
|
command
|
79
77
|
end
|
@@ -88,7 +86,7 @@ module Backup
|
|
88
86
|
# Builds a compatible string for the additional options
|
89
87
|
# specified by the user
|
90
88
|
def user_options
|
91
|
-
slapcat_args.join(
|
89
|
+
slapcat_args.join(" ")
|
92
90
|
end
|
93
91
|
end
|
94
92
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Database
|
5
3
|
class PostgreSQL < Base
|
@@ -53,17 +51,19 @@ module Backup
|
|
53
51
|
super
|
54
52
|
|
55
53
|
pipeline = Pipeline.new
|
56
|
-
dump_ext =
|
54
|
+
dump_ext = "sql"
|
57
55
|
|
58
56
|
pipeline << (dump_all? ? pgdumpall : pgdump)
|
59
57
|
|
60
|
-
model.compressor
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
if model.compressor
|
59
|
+
model.compressor.compress_with do |command, ext|
|
60
|
+
pipeline << command
|
61
|
+
dump_ext << ext
|
62
|
+
end
|
63
|
+
end
|
64
64
|
|
65
|
-
pipeline << "#{
|
66
|
-
|
65
|
+
pipeline << "#{utility(:cat)} > " \
|
66
|
+
"'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
|
67
67
|
|
68
68
|
pipeline.run
|
69
69
|
if pipeline.success?
|
@@ -74,60 +74,59 @@ module Backup
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def pgdump
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
password_option.to_s +
|
78
|
+
sudo_option.to_s +
|
79
|
+
"#{utility(:pg_dump)} #{username_option} #{connectivity_options} " \
|
80
|
+
"#{user_options} #{tables_to_dump} #{tables_to_skip} #{name}"
|
81
81
|
end
|
82
82
|
|
83
83
|
def pgdumpall
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
password_option.to_s +
|
85
|
+
sudo_option.to_s +
|
86
|
+
"#{utility(:pg_dumpall)} #{username_option} " \
|
87
|
+
"#{connectivity_options} #{user_options}"
|
88
88
|
end
|
89
89
|
|
90
90
|
def password_option
|
91
|
-
"PGPASSWORD=#{
|
91
|
+
"PGPASSWORD=#{Shellwords.escape(password)} " if password
|
92
92
|
end
|
93
93
|
|
94
94
|
def sudo_option
|
95
|
-
"#{
|
95
|
+
"#{utility(:sudo)} -n -H -u #{sudo_user} " if sudo_user
|
96
96
|
end
|
97
97
|
|
98
98
|
def username_option
|
99
|
-
"--username=#{
|
99
|
+
"--username=#{Shellwords.escape(username)}" if username
|
100
100
|
end
|
101
101
|
|
102
102
|
def connectivity_options
|
103
|
-
return "--host='#{
|
103
|
+
return "--host='#{socket}'" if socket
|
104
104
|
|
105
105
|
opts = []
|
106
|
-
opts << "--host='#{
|
107
|
-
opts << "--port='#{
|
108
|
-
opts.join(
|
106
|
+
opts << "--host='#{host}'" if host
|
107
|
+
opts << "--port='#{port}'" if port
|
108
|
+
opts.join(" ")
|
109
109
|
end
|
110
110
|
|
111
111
|
def user_options
|
112
|
-
Array(additional_options).join(
|
112
|
+
Array(additional_options).join(" ")
|
113
113
|
end
|
114
114
|
|
115
115
|
def tables_to_dump
|
116
116
|
Array(only_tables).map do |table|
|
117
|
-
"--table='#{
|
118
|
-
end.join(
|
117
|
+
"--table='#{table}'"
|
118
|
+
end.join(" ")
|
119
119
|
end
|
120
120
|
|
121
121
|
def tables_to_skip
|
122
122
|
Array(skip_tables).map do |table|
|
123
|
-
"--exclude-table='#{
|
124
|
-
end.join(
|
123
|
+
"--exclude-table='#{table}'"
|
124
|
+
end.join(" ")
|
125
125
|
end
|
126
126
|
|
127
127
|
def dump_all?
|
128
128
|
name == :all
|
129
129
|
end
|
130
|
-
|
131
130
|
end
|
132
131
|
end
|
133
132
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Database
|
5
3
|
class Redis < Base
|
@@ -59,12 +57,10 @@ module Backup
|
|
59
57
|
|
60
58
|
@mode ||= :copy
|
61
59
|
|
62
|
-
unless MODES.include?(mode)
|
63
|
-
raise Error, "'#{ mode }' is not a valid mode"
|
64
|
-
end
|
60
|
+
raise Error, "'#{mode}' is not a valid mode" unless MODES.include?(mode)
|
65
61
|
|
66
62
|
if mode == :copy && rdb_path.nil?
|
67
|
-
raise Error,
|
63
|
+
raise Error, "`rdb_path` must be set when `mode` is :copy"
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
@@ -96,17 +92,19 @@ module Backup
|
|
96
92
|
|
97
93
|
def sync!
|
98
94
|
pipeline = Pipeline.new
|
99
|
-
dump_ext =
|
95
|
+
dump_ext = "rdb"
|
100
96
|
|
101
|
-
pipeline << "#{
|
97
|
+
pipeline << "#{redis_cli_cmd} --rdb -"
|
102
98
|
|
103
|
-
model.compressor
|
104
|
-
|
105
|
-
|
106
|
-
|
99
|
+
if model.compressor
|
100
|
+
model.compressor.compress_with do |command, ext|
|
101
|
+
pipeline << command
|
102
|
+
dump_ext << ext
|
103
|
+
end
|
104
|
+
end
|
107
105
|
|
108
|
-
pipeline << "#{
|
109
|
-
|
106
|
+
pipeline << "#{utility(:cat)} > " \
|
107
|
+
"'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
|
110
108
|
|
111
109
|
pipeline.run
|
112
110
|
|
@@ -116,17 +114,16 @@ module Backup
|
|
116
114
|
end
|
117
115
|
|
118
116
|
def save!
|
119
|
-
resp = run("#{
|
117
|
+
resp = run("#{redis_cli_cmd} SAVE")
|
120
118
|
unless resp =~ /OK$/
|
121
119
|
raise Error, <<-EOS
|
122
120
|
Failed to invoke the `SAVE` command
|
123
|
-
Response was: #{
|
121
|
+
Response was: #{resp}
|
124
122
|
EOS
|
125
123
|
end
|
126
|
-
|
127
124
|
rescue Error
|
128
125
|
if resp =~ /save already in progress/
|
129
|
-
unless (attempts ||=
|
126
|
+
unless (attempts ||= "0").next! == "5"
|
130
127
|
sleep 5
|
131
128
|
retry
|
132
129
|
end
|
@@ -138,14 +135,14 @@ module Backup
|
|
138
135
|
unless File.exist?(rdb_path)
|
139
136
|
raise Error, <<-EOS
|
140
137
|
Redis database dump not found
|
141
|
-
`rdb_path` was '#{
|
138
|
+
`rdb_path` was '#{rdb_path}'
|
142
139
|
EOS
|
143
140
|
end
|
144
141
|
|
145
|
-
dst_path = File.join(dump_path, dump_filename +
|
142
|
+
dst_path = File.join(dump_path, dump_filename + ".rdb")
|
146
143
|
if model.compressor
|
147
144
|
model.compressor.compress_with do |command, ext|
|
148
|
-
run("#{
|
145
|
+
run("#{command} -c '#{rdb_path}' > '#{dst_path + ext}'")
|
149
146
|
end
|
150
147
|
else
|
151
148
|
FileUtils.cp(rdb_path, dst_path)
|
@@ -153,27 +150,27 @@ module Backup
|
|
153
150
|
end
|
154
151
|
|
155
152
|
def redis_cli_cmd
|
156
|
-
"#{
|
157
|
-
|
153
|
+
"#{utility("redis-cli")} #{password_option} " \
|
154
|
+
"#{connectivity_options} #{user_options}"
|
158
155
|
end
|
159
156
|
|
160
157
|
def password_option
|
161
|
-
|
158
|
+
return unless password
|
159
|
+
"-a '#{password}'"
|
162
160
|
end
|
163
161
|
|
164
162
|
def connectivity_options
|
165
|
-
return "-s '#{
|
163
|
+
return "-s '#{socket}'" if socket
|
166
164
|
|
167
165
|
opts = []
|
168
|
-
opts << "-h '#{
|
169
|
-
opts << "-p '#{
|
170
|
-
opts.join(
|
166
|
+
opts << "-h '#{host}'" if host
|
167
|
+
opts << "-p '#{port}'" if port
|
168
|
+
opts.join(" ")
|
171
169
|
end
|
172
170
|
|
173
171
|
def user_options
|
174
|
-
Array(additional_options).join(
|
172
|
+
Array(additional_options).join(" ")
|
175
173
|
end
|
176
|
-
|
177
174
|
end
|
178
175
|
end
|
179
176
|
end
|
data/lib/backup/database/riak.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Database
|
5
3
|
class Riak < Base
|
6
|
-
|
7
4
|
##
|
8
5
|
# Node is the node from which to perform the backup.
|
9
6
|
# Default: riak@127.0.0.1
|
@@ -23,9 +20,9 @@ module Backup
|
|
23
20
|
super
|
24
21
|
instance_eval(&block) if block_given?
|
25
22
|
|
26
|
-
@node ||=
|
27
|
-
@cookie ||=
|
28
|
-
@user ||=
|
23
|
+
@node ||= "riak@127.0.0.1"
|
24
|
+
@cookie ||= "riak"
|
25
|
+
@user ||= "riak"
|
29
26
|
end
|
30
27
|
|
31
28
|
##
|
@@ -38,14 +35,16 @@ module Backup
|
|
38
35
|
|
39
36
|
dump_file = File.join(dump_path, dump_filename)
|
40
37
|
with_riak_owned_dump_path do
|
41
|
-
run("#{
|
38
|
+
run("#{riakadmin} backup #{node} #{cookie} '#{dump_file}' node")
|
42
39
|
end
|
43
40
|
|
44
|
-
model.compressor
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
if model.compressor
|
42
|
+
model.compressor.compress_with do |command, ext|
|
43
|
+
dump_file << "-#{node}" # `riak-admin` appends `node` to the filename.
|
44
|
+
run("#{command} -c '#{dump_file}' > '#{dump_file + ext}'")
|
45
|
+
FileUtils.rm_f(dump_file)
|
46
|
+
end
|
47
|
+
end
|
49
48
|
|
50
49
|
log!(:finished)
|
51
50
|
end
|
@@ -61,22 +60,20 @@ module Backup
|
|
61
60
|
# the user running Backup, since the absence of the execute bit on their
|
62
61
|
# home directory would deny +user+ access.
|
63
62
|
def with_riak_owned_dump_path
|
64
|
-
run
|
65
|
-
"#{ user } '#{ dump_path }'")
|
63
|
+
run "#{utility(:sudo)} -n #{utility(:chown)} #{user} '#{dump_path}'"
|
66
64
|
yield
|
67
65
|
ensure
|
68
66
|
# reclaim ownership
|
69
|
-
run
|
70
|
-
|
67
|
+
run "#{utility(:sudo)} -n #{utility(:chown)} -R " \
|
68
|
+
"#{Config.user} '#{dump_path}'"
|
71
69
|
end
|
72
70
|
|
73
71
|
##
|
74
72
|
# `riak-admin` must be run as the riak +user+.
|
75
73
|
# It will do this itself, but without `-n` and emits a message on STDERR.
|
76
74
|
def riakadmin
|
77
|
-
"#{
|
75
|
+
"#{utility(:sudo)} -n -u #{user} #{utility("riak-admin")}"
|
78
76
|
end
|
79
|
-
|
80
77
|
end
|
81
78
|
end
|
82
79
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Database
|
5
3
|
class SQLite < Base
|
@@ -28,10 +26,10 @@ module Backup
|
|
28
26
|
def perform!
|
29
27
|
super
|
30
28
|
|
31
|
-
dump = "echo '.dump' | #{
|
29
|
+
dump = "echo '.dump' | #{sqlitedump_utility} #{path}"
|
32
30
|
|
33
31
|
pipeline = Pipeline.new
|
34
|
-
dump_ext =
|
32
|
+
dump_ext = "sql"
|
35
33
|
|
36
34
|
pipeline << dump
|
37
35
|
if model.compressor
|
@@ -41,7 +39,7 @@ module Backup
|
|
41
39
|
end
|
42
40
|
end
|
43
41
|
|
44
|
-
pipeline << "cat > '#{
|
42
|
+
pipeline << "cat > '#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
|
45
43
|
|
46
44
|
pipeline.run
|
47
45
|
|
@@ -49,7 +47,7 @@ module Backup
|
|
49
47
|
log!(:finished)
|
50
48
|
else
|
51
49
|
raise Error,
|
52
|
-
"#{
|
50
|
+
"#{database_name} Dump Failed!\n" + pipeline.error_messages
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Encryptor
|
5
3
|
class Base
|
@@ -15,14 +13,14 @@ module Backup
|
|
15
13
|
##
|
16
14
|
# Return the encryptor name, with Backup namespace removed
|
17
15
|
def encryptor_name
|
18
|
-
self.class.to_s.sub(
|
16
|
+
self.class.to_s.sub("Backup::", "")
|
19
17
|
end
|
20
18
|
|
21
19
|
##
|
22
20
|
# Logs a message to the console and log file to inform
|
23
21
|
# the client that Backup is encrypting the archive
|
24
22
|
def log!
|
25
|
-
Logger.info "Using #{
|
23
|
+
Logger.info "Using #{encryptor_name} to encrypt the archive."
|
26
24
|
end
|
27
25
|
end
|
28
26
|
end
|
data/lib/backup/encryptor/gpg.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Encryptor
|
5
3
|
##
|
@@ -104,7 +102,7 @@ module Backup
|
|
104
102
|
attr_reader :mode
|
105
103
|
def mode=(mode)
|
106
104
|
@mode = mode.to_sym
|
107
|
-
raise Error, "'#{
|
105
|
+
raise Error, "'#{@mode}' is not a valid mode." unless MODES.include?(@mode)
|
108
106
|
end
|
109
107
|
|
110
108
|
##
|
@@ -411,11 +409,10 @@ module Backup
|
|
411
409
|
prepare
|
412
410
|
|
413
411
|
if mode_options.empty?
|
414
|
-
raise Error, "Encryption could not be performed for mode '#{
|
412
|
+
raise Error, "Encryption could not be performed for mode '#{mode}'"
|
415
413
|
end
|
416
414
|
|
417
|
-
yield "#{
|
418
|
-
|
415
|
+
yield "#{utility(:gpg)} #{base_options} #{mode_options}", ".gpg"
|
419
416
|
ensure
|
420
417
|
cleanup
|
421
418
|
end
|
@@ -426,7 +423,7 @@ module Backup
|
|
426
423
|
# Remove any temporary directories and reset all instance variables.
|
427
424
|
#
|
428
425
|
def prepare
|
429
|
-
FileUtils.rm_rf(@tempdirs, :
|
426
|
+
FileUtils.rm_rf(@tempdirs, secure: true) if @tempdirs
|
430
427
|
@tempdirs = []
|
431
428
|
@base_options = nil
|
432
429
|
@mode_options = nil
|
@@ -444,12 +441,12 @@ module Backup
|
|
444
441
|
#
|
445
442
|
def base_options
|
446
443
|
@base_options ||= begin
|
447
|
-
opts = [
|
444
|
+
opts = ["--no-tty"]
|
448
445
|
path = setup_gpg_homedir
|
449
|
-
opts << "--homedir '#{
|
446
|
+
opts << "--homedir '#{path}'" if path
|
450
447
|
path = setup_gpg_config
|
451
|
-
opts << "--options '#{
|
452
|
-
opts.join(
|
448
|
+
opts << "--options '#{path}'" if path
|
449
|
+
opts.join(" ")
|
453
450
|
end
|
454
451
|
end
|
455
452
|
|
@@ -470,18 +467,17 @@ module Backup
|
|
470
467
|
path = File.expand_path(gpg_homedir)
|
471
468
|
FileUtils.mkdir_p(path)
|
472
469
|
FileUtils.chown(Config.user, nil, path)
|
473
|
-
FileUtils.chmod(
|
470
|
+
FileUtils.chmod(0o700, path)
|
474
471
|
|
475
|
-
unless %w
|
476
|
-
all? {|name| File.exist? File.join(path, name) }
|
477
|
-
run("#{
|
472
|
+
unless %w[pubring.gpg secring.gpg trustdb.gpg]
|
473
|
+
.all? { |name| File.exist? File.join(path, name) }
|
474
|
+
run("#{utility(:gpg)} --homedir '#{path}' -K 2>&1 >/dev/null")
|
478
475
|
end
|
479
476
|
|
480
477
|
path
|
481
|
-
|
482
478
|
rescue => err
|
483
|
-
raise Error.wrap
|
484
|
-
|
479
|
+
raise Error.wrap \
|
480
|
+
err, "Failed to create or set permissions for #gpg_homedir"
|
485
481
|
end
|
486
482
|
|
487
483
|
##
|
@@ -501,16 +497,15 @@ module Backup
|
|
501
497
|
def setup_gpg_config
|
502
498
|
return false unless gpg_config
|
503
499
|
|
504
|
-
dir = Dir.mktmpdir(
|
500
|
+
dir = Dir.mktmpdir("backup-gpg_config", Config.tmp_path)
|
505
501
|
@tempdirs << dir
|
506
|
-
file = Tempfile.open(
|
507
|
-
file.write gpg_config.gsub(/^[[:blank:]]+/,
|
502
|
+
file = Tempfile.open("backup-gpg_config", dir)
|
503
|
+
file.write gpg_config.gsub(/^[[:blank:]]+/, "")
|
508
504
|
file.close
|
509
505
|
|
510
506
|
check_gpg_config(file.path)
|
511
507
|
|
512
508
|
file.path
|
513
|
-
|
514
509
|
rescue => err
|
515
510
|
cleanup
|
516
511
|
raise Error.wrap(err, "Error creating temporary file for #gpg_config.")
|
@@ -524,7 +519,7 @@ module Backup
|
|
524
519
|
#
|
525
520
|
def check_gpg_config(path)
|
526
521
|
ret = run(
|
527
|
-
"#{
|
522
|
+
"#{utility(:gpg)} --options '#{path}' --gpgconf-test 2>&1"
|
528
523
|
).chomp
|
529
524
|
raise ret unless ret.empty?
|
530
525
|
end
|
@@ -537,7 +532,7 @@ module Backup
|
|
537
532
|
@mode_options ||= begin
|
538
533
|
s_opts = symmetric_options if mode != :asymmetric
|
539
534
|
a_opts = asymmetric_options if mode != :symmetric
|
540
|
-
[s_opts, a_opts].compact.join(
|
535
|
+
[s_opts, a_opts].compact.join(" ")
|
541
536
|
end
|
542
537
|
end
|
543
538
|
|
@@ -555,7 +550,7 @@ module Backup
|
|
555
550
|
end
|
556
551
|
|
557
552
|
if path && File.exist?(path)
|
558
|
-
"-c --passphrase-file '#{
|
553
|
+
"-c --passphrase-file '#{path}'"
|
559
554
|
else
|
560
555
|
Logger.warn("Symmetric encryption options could not be set.")
|
561
556
|
nil
|
@@ -570,14 +565,13 @@ module Backup
|
|
570
565
|
def setup_passphrase_file
|
571
566
|
return false if passphrase.to_s.empty?
|
572
567
|
|
573
|
-
dir = Dir.mktmpdir(
|
568
|
+
dir = Dir.mktmpdir("backup-gpg_passphrase", Config.tmp_path)
|
574
569
|
@tempdirs << dir
|
575
|
-
file = Tempfile.open(
|
570
|
+
file = Tempfile.open("backup-gpg_passphrase", dir)
|
576
571
|
file.write passphrase.to_s
|
577
572
|
file.close
|
578
573
|
|
579
574
|
file.path
|
580
|
-
|
581
575
|
rescue => err
|
582
576
|
Logger.warn Error.wrap(err, "Error creating temporary passphrase file.")
|
583
577
|
false
|
@@ -595,7 +589,7 @@ module Backup
|
|
595
589
|
else
|
596
590
|
# skip trust database checks
|
597
591
|
"-e --trust-model always " +
|
598
|
-
|
592
|
+
user_recipients.map { |r| "-r '#{r}'" }.join(" ")
|
599
593
|
end
|
600
594
|
end
|
601
595
|
|
@@ -622,9 +616,8 @@ module Backup
|
|
622
616
|
# will log a warning and return nil if the import fails
|
623
617
|
import_key(identifier, key)
|
624
618
|
else
|
625
|
-
Logger.warn
|
626
|
-
"No public key was found in #keys for '#{
|
627
|
-
)
|
619
|
+
Logger.warn \
|
620
|
+
"No public key was found in #keys for '#{identifier}'"
|
628
621
|
nil
|
629
622
|
end
|
630
623
|
end
|
@@ -640,10 +633,11 @@ module Backup
|
|
640
633
|
def user_keys
|
641
634
|
@user_keys ||= begin
|
642
635
|
_keys = keys || {}
|
643
|
-
ret = Hash[_keys.map {|k,v| [clean_identifier(k), v] }]
|
644
|
-
|
645
|
-
|
646
|
-
|
636
|
+
ret = Hash[_keys.map { |k, v| [clean_identifier(k), v] }]
|
637
|
+
if ret.keys.count != _keys.keys.count
|
638
|
+
Logger.warn \
|
639
|
+
"Duplicate public key identifiers were detected in #keys."
|
640
|
+
end
|
647
641
|
ret
|
648
642
|
end
|
649
643
|
end
|
@@ -654,8 +648,8 @@ module Backup
|
|
654
648
|
# and wrap email addresses in <> to perform exact matching.
|
655
649
|
#
|
656
650
|
def clean_identifier(str)
|
657
|
-
str = str.to_s.gsub(/[[:blank:]]+/,
|
658
|
-
str =~ /@/ ? "<#{
|
651
|
+
str = str.to_s.gsub(/[[:blank:]]+/, "")
|
652
|
+
str =~ /@/ ? "<#{str.gsub(/(<|>)/, "")}>" : str.upcase
|
659
653
|
end
|
660
654
|
|
661
655
|
##
|
@@ -664,22 +658,20 @@ module Backup
|
|
664
658
|
# Note that errors raised by Cli::Helpers#run may also be rescued here.
|
665
659
|
#
|
666
660
|
def import_key(identifier, key)
|
667
|
-
file = Tempfile.open(
|
668
|
-
file.write(key.gsub(/^[[:blank:]]+/,
|
661
|
+
file = Tempfile.open("backup-gpg_import", Config.tmp_path)
|
662
|
+
file.write(key.gsub(/^[[:blank:]]+/, ""))
|
669
663
|
file.close
|
670
|
-
ret = run(
|
671
|
-
"
|
672
|
-
"--keyid-format 0xlong --import '#{ file.path }' 2>&1"
|
673
|
-
)
|
664
|
+
ret = run "#{utility(:gpg)} #{base_options} " \
|
665
|
+
"--keyid-format 0xlong --import '#{file.path}' 2>&1"
|
674
666
|
file.delete
|
675
667
|
|
676
668
|
keyid = ret.match(/ 0x(\w{16})/).to_a[1]
|
677
|
-
raise "GPG Returned:\n#{
|
669
|
+
raise "GPG Returned:\n#{ret.gsub(/^\s*/, " ")}" unless keyid
|
678
670
|
keyid
|
679
|
-
|
680
671
|
rescue => err
|
681
672
|
Logger.warn Error.wrap(
|
682
|
-
|
673
|
+
err, "Public key import failed for '#{identifier}'"
|
674
|
+
)
|
683
675
|
nil
|
684
676
|
end
|
685
677
|
|
@@ -691,20 +683,17 @@ module Backup
|
|
691
683
|
def system_identifiers
|
692
684
|
@system_identifiers ||= begin
|
693
685
|
skip_key = false
|
694
|
-
data = run(
|
695
|
-
"#{ utility(:gpg) } #{ base_options } " +
|
686
|
+
data = run "#{utility(:gpg)} #{base_options} " \
|
696
687
|
"--with-colons --fixed-list-mode --fingerprint"
|
697
|
-
)
|
698
688
|
data.lines.map do |line|
|
699
689
|
line.strip!
|
700
690
|
|
701
691
|
# process public key record
|
702
692
|
if line =~ /^pub:/
|
703
|
-
validity, keyid, capabilities =
|
704
|
-
line.split(':').values_at(1, 4, 11)
|
693
|
+
validity, keyid, capabilities = line.split(":").values_at(1, 4, 11)
|
705
694
|
# skip keys marked as revoked ('r'), expired ('e'),
|
706
695
|
# invalid ('i') or disabled ('D')
|
707
|
-
if validity[0,1] =~ /(r|e|i)/ || capabilities =~ /D/
|
696
|
+
if validity[0, 1] =~ /(r|e|i)/ || capabilities =~ /D/
|
708
697
|
skip_key = true
|
709
698
|
next nil
|
710
699
|
else
|
@@ -714,26 +703,28 @@ module Backup
|
|
714
703
|
end
|
715
704
|
else
|
716
705
|
# wait for the next valid public key record
|
717
|
-
next
|
706
|
+
next if skip_key
|
718
707
|
|
719
708
|
# process UID records for the current public key
|
720
709
|
if line =~ /^uid:/
|
721
|
-
validity, userid = line.split(
|
710
|
+
validity, userid = line.split(":").values_at(1, 9)
|
722
711
|
# skip records marked as revoked ('r'), expired ('e')
|
723
712
|
# or invalid ('i')
|
724
713
|
if validity !~ /(r|e|i)/
|
725
714
|
# return the last email found in user id string,
|
726
715
|
# since this includes user supplied comments.
|
727
716
|
# return nil if no email found.
|
728
|
-
email
|
717
|
+
email = nil
|
718
|
+
str = userid
|
729
719
|
while match = str.match(/<.+?@.+?>/)
|
730
|
-
email
|
720
|
+
email = match[0]
|
721
|
+
str = match.post_match
|
731
722
|
end
|
732
723
|
next email
|
733
724
|
end
|
734
725
|
# return public key's fingerprint
|
735
726
|
elsif line =~ /^fpr:/
|
736
|
-
next line.split(
|
727
|
+
next line.split(":")[9]
|
737
728
|
end
|
738
729
|
|
739
730
|
nil # ignore any other lines
|
@@ -741,7 +732,6 @@ module Backup
|
|
741
732
|
end.flatten.compact
|
742
733
|
end
|
743
734
|
end
|
744
|
-
|
745
735
|
end
|
746
736
|
end
|
747
737
|
end
|