backup 4.4.1 → 5.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|