backup-remote 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c0aa4fc4049cb34499a58fe72e4ebe283b950063
4
- data.tar.gz: c6b09225ac5faf3de22f73cf63fc801400cdfa3d
3
+ metadata.gz: c2fc3569f37e204350b31fc06bf5edde0f15a89e
4
+ data.tar.gz: dd9db17395b314a876850f35aecc9bea56ea5c0d
5
5
  SHA512:
6
- metadata.gz: 9c7162a70d86ae53879c7733a479985c34fcaea433dc2fcf1ed2ed2db26c51bfece25f6970fc10166d4ce0b5966a267089e13b473c66c8168aff8c5c7565e017
7
- data.tar.gz: 52b99fefd11d57b4a03b6dca9744c7d26e605fc8f8db5b30dd4ea988a2735d43359659c2af5025861f9724f60edc6f32794fa513c469201e9f3855e21db240f0
6
+ metadata.gz: 7e6bdac05bd47da6f855508e369576f59011058ea55cccad9b856b0514158786730a6207d1f0be31371c9fd7055913c1a8d7e5688c0fce0f74fdecec050b8a99
7
+ data.tar.gz: a40f18efe432ad1e45297e04549070435a6d487e55bc673e44fdb00ca3cf17a143c031bdbf95bf0c1a780f0140d0d7e48e58b9233ff7f515b4f5f87093456609
data/README.md CHANGED
@@ -12,6 +12,12 @@ Backup is a system utility for Linux and Mac OS X, distributed as a RubyGem, tha
12
12
  operations. It provides an elegant DSL in Ruby for _modeling_ your backups.
13
13
  Backup has built-in support for various databases, storage protocols/services, syncers, compressors, encryptors and notifiers which you can mix and match.
14
14
 
15
+ The gem adds the following model components:
16
+ * Remote Archive
17
+ * Remote MySQL database
18
+ * Remote data
19
+
20
+
15
21
 
16
22
 
17
23
  # How it works
@@ -67,11 +73,34 @@ Options for SSH connection:
67
73
 
68
74
  ## Archive files on a remote server
69
75
 
70
- * Use RemoteArchive
76
+ * Use remote_archive in your model
77
+
71
78
 
72
79
  ```
80
+
81
+ Model.new(:my_server_files_backup, 'Backup files') do
82
+
83
+ remote_archive :files do |archive|
84
+ archive.server_host = "myserver.com"
85
+ archive.server_ssh_user = "user"
86
+ archive.server_ssh_password = "pwd"
87
+
88
+
89
+ # archive options - the same options as for archive
90
+ # see http://backup.github.io/backup/v4/archives/
91
+
92
+
93
+ end
94
+
95
+ ...
96
+
97
+ end
98
+
73
99
  ```
74
100
 
101
+ Options:
102
+ * server_command - command to create archive file
103
+
75
104
 
76
105
  # Databases
77
106
 
@@ -80,6 +109,7 @@ Options for SSH connection:
80
109
  * Now it is implemented the following databases:
81
110
  * RemoteMySQL
82
111
 
112
+
83
113
  ### RemoteMySQL
84
114
 
85
115
  ```
@@ -97,13 +127,45 @@ Model.new(:my_backup, 'My Backup') do
97
127
  ...
98
128
  end
99
129
  ..
100
- end
101
-
130
+ end
131
+
102
132
  ````
103
133
 
104
- # Custom backup command
105
134
 
106
- * Run custom command to create a backup archive on a remote server
135
+
136
+ # Custom data on remote server
137
+
138
+ * Run custom command on the remote server to create a backup archive
139
+
140
+ * Specify command to run to generate archive file on the remote server
141
+
142
+ * This command should create an archive file with filename specified in server_path option.
143
+
144
+
145
+ ```
146
+ Model.new(:my_server_data_backup, 'Backup data') do
147
+
148
+ remote_data :mydata do |archive|
149
+ archive.server_host = "myserver.com"
150
+ archive.server_ssh_user = "user"
151
+ archive.server_ssh_password = "pwd"
152
+
153
+
154
+ archive.command = "--any command to generate backup archive file--"
155
+ # archive.command = "echo '1' > /tmp/backup.txt"
156
+
157
+ archive.server_path = "/path/to/archive.tar.gz"
158
+ # archive.command = "/tmp/backup.txt"
159
+
160
+
161
+
162
+ end
163
+
164
+ ...
165
+
166
+ end
167
+
168
+ ```
107
169
 
108
170
 
109
171
  # Backup gem
data/lib/backup.rb CHANGED
@@ -149,7 +149,13 @@ module Backup
149
149
  pipeline
150
150
  splitter
151
151
  template
152
+
153
+ remote_archive
154
+ remote_data
155
+
152
156
  version
153
- }.each {|lib| require File.join(LIBRARY_PATH, lib) }
157
+ }.each do |lib|
158
+ require File.join(LIBRARY_PATH, lib)
159
+ end
154
160
 
155
161
  end
data/lib/backup/model.rb CHANGED
@@ -139,6 +139,19 @@ module Backup
139
139
  @archives << Archive.new(self, name, &block)
140
140
  end
141
141
 
142
+ ##
143
+ # Adds a Remote Archive. Multiple archives may be added to the model.
144
+ def remote_archive(name, &block)
145
+ @archives << RemoteArchive.new(self, name, &block)
146
+ end
147
+
148
+ ##
149
+ # Adds a Remote data Archive
150
+ def remote_data(name, &block)
151
+ @archives << RemoteData.new(self, name, &block)
152
+ end
153
+
154
+
142
155
  ##
143
156
  # Adds an Database. Multiple Databases may be added to the model.
144
157
  def database(name, database_id = nil, &block)
@@ -77,6 +77,39 @@ module Backup
77
77
  }
78
78
  end
79
79
 
80
+ def ssh_upload_file(hostname, ssh_user, ssh_pass, source_file, dest_file, handler=nil)
81
+ host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
82
+ host.password = ssh_pass
83
+
84
+ # scp
85
+ f_temp = "/tmp/#{SecureRandom.uuid}"
86
+
87
+ # sshkit
88
+ on host do |host|
89
+ as(user: ssh_user) do
90
+
91
+ end
92
+
93
+ # NOT WORK with sudo
94
+ #upload! source_file, dest_file
95
+
96
+ # upload to temp file
97
+ upload! source_file, f_temp
98
+
99
+ # upload to dest
100
+ execute("cp #{f_temp} #{dest_file}", interaction_handler: handler)
101
+
102
+ end
103
+
104
+ #
105
+ return {res: 1, output: ""}
106
+ rescue => e
107
+ {
108
+ res: 0,
109
+ error: e.message
110
+ }
111
+ end
112
+
80
113
  end
81
114
  end
82
115
  end
@@ -0,0 +1,266 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/ssh'
4
+
5
+ require 'sshkit'
6
+ require 'sshkit/dsl'
7
+ require 'sshkit/sudo'
8
+
9
+
10
+ module Backup
11
+ class RemoteArchive < Archive
12
+ class Error < Backup::Error; end
13
+
14
+ include Utilities::Helpers
15
+ #attr_reader :name, :options
16
+
17
+ include SSHKit::DSL
18
+
19
+ # server options
20
+ attr_accessor :server_host
21
+ attr_accessor :server_ssh_user
22
+ attr_accessor :server_ssh_password
23
+ attr_accessor :server_ssh_key
24
+ attr_accessor :server_backup_path
25
+
26
+
27
+
28
+ ##
29
+ # Adds a new Archive to a Backup Model.
30
+ #
31
+ # Backup::Model.new(:my_backup, 'My Backup') do
32
+ # archive :my_archive do |archive|
33
+ # archive.add 'path/to/archive'
34
+ # archive.add '/another/path/to/archive'
35
+ # archive.exclude 'path/to/exclude'
36
+ # archive.exclude '/another/path/to/exclude'
37
+ # end
38
+ # end
39
+ #
40
+ # All paths added using `add` or `exclude` will be expanded to their
41
+ # full paths from the root of the filesystem. Files will be added to
42
+ # the tar archive using these full paths, and their leading `/` will
43
+ # be preserved (using tar's `-P` option).
44
+ #
45
+ # /path/to/pwd/path/to/archive/...
46
+ # /another/path/to/archive/...
47
+ #
48
+ # When a `root` path is given, paths to add/exclude are taken as
49
+ # relative to the `root` path, unless given as absolute paths.
50
+ #
51
+ # Backup::Model.new(:my_backup, 'My Backup') do
52
+ # archive :my_archive do |archive|
53
+ # archive.root '~/my_data'
54
+ # archive.add 'path/to/archive'
55
+ # archive.add '/another/path/to/archive'
56
+ # archive.exclude 'path/to/exclude'
57
+ # archive.exclude '/another/path/to/exclude'
58
+ # end
59
+ # end
60
+ #
61
+ # This directs `tar` to change directories to the `root` path to create
62
+ # the archive. Unless paths were given as absolute, the paths within the
63
+ # archive will be relative to the `root` path.
64
+ #
65
+ # path/to/archive/...
66
+ # /another/path/to/archive/...
67
+ #
68
+ # For absolute paths added to this archive, the leading `/` will be
69
+ # preserved. Take note that when archives are extracted, leading `/` are
70
+ # stripped by default, so care must be taken when extracting archives with
71
+ # mixed relative/absolute paths.
72
+ def initialize(model, name, &block)
73
+ @model = model
74
+ @name = name.to_s
75
+ @options = {
76
+ :sudo => false,
77
+ :root => false,
78
+ :paths => [],
79
+ :excludes => [],
80
+ :tar_options => ''
81
+ }
82
+
83
+ DSL.new(@options).instance_eval(&block)
84
+
85
+ #
86
+ self.server_host = @options[:server_host]
87
+ self.server_ssh_user = @options[:server_ssh_user]
88
+ self.server_ssh_password = @options[:server_ssh_password]
89
+ end
90
+
91
+ def perform!
92
+ Logger.info "Creating Archive '#{ name }'..."
93
+
94
+ #
95
+ path = File.join(Config.tmp_path, @model.trigger, 'archives')
96
+ FileUtils.mkdir_p(path)
97
+
98
+
99
+ #
100
+ remote = Backup::Remote::Command.new
101
+
102
+ pipeline = Pipeline.new
103
+ with_files_from(paths_to_package) do |files_from|
104
+ # upload to server
105
+ res_upload = remote.ssh_upload_file(server_host, server_ssh_user, server_ssh_password, files_from, files_from)
106
+
107
+ if res_upload[:res]==0
108
+ raise 'Cannot upload file from server - #{files_from}'
109
+ end
110
+
111
+ #
112
+ pipeline.add(
113
+ "#{ tar_command } #{ tar_options } -cPf -#{ tar_root } " +
114
+ "#{ paths_to_exclude } -T '#{ files_from }'",
115
+ tar_success_codes
116
+ )
117
+
118
+ extension = 'tar'
119
+ @model.compressor.compress_with do |command, ext|
120
+ pipeline << command
121
+ extension << ext
122
+ end if @model.compressor
123
+
124
+ #
125
+ archive_file = File.join(path, "#{ name }.#{ extension }")
126
+ remote_archive_file = File.join('/tmp', "#{ name }.#{ extension }")
127
+ pipeline << "#{ utility(:cat) } > '#{ remote_archive_file }'"
128
+
129
+
130
+ #pipeline.run
131
+
132
+ # generate backup on remote server
133
+ cmd_remote = pipeline.commands.join(" | ")
134
+
135
+ #puts "remote cmd: #{cmd_remote}"
136
+ #exit
137
+
138
+
139
+ res_generate = remote.run_ssh_cmd(server_host, server_ssh_user, server_ssh_password, cmd_remote)
140
+
141
+ if res_generate[:res]==0
142
+ raise 'Cannot create backup on server'
143
+ end
144
+
145
+ # download backup
146
+ res_download = remote.ssh_download_file(server_host, server_ssh_user, server_ssh_password, remote_archive_file, archive_file)
147
+
148
+ #puts "res: #{res_download}"
149
+
150
+ if res_download[:res]==0
151
+ raise 'Cannot download file from server'
152
+ end
153
+
154
+ # delete archive on server
155
+ res_delete = remote.run_ssh_cmd(server_host, server_ssh_user, server_ssh_password, "rm #{remote_archive_file}")
156
+
157
+ end
158
+
159
+ Logger.info "Archive '#{ name }' Complete!"
160
+
161
+ #if pipeline.success?
162
+ # Logger.info "Archive '#{ name }' Complete!"
163
+ #else
164
+ # raise Error, "Failed to Create Archive '#{ name }'\n" + pipeline.error_messages
165
+ #end
166
+ end
167
+
168
+ private
169
+
170
+ def tar_command
171
+ tar = utility(:tar)
172
+ options[:sudo] ? "#{ utility(:sudo) } -n #{ tar }" : tar
173
+ end
174
+
175
+ def tar_root
176
+ options[:root] ? " -C '#{ File.expand_path(options[:root]) }'" : ''
177
+ end
178
+
179
+ def paths_to_package
180
+ options[:paths].map {|path| prepare_path(path) }
181
+ end
182
+
183
+ def with_files_from(paths)
184
+ tmpfile = Tempfile.new('backup-archive-paths')
185
+ paths.each {|path| tmpfile.puts path }
186
+ tmpfile.close
187
+
188
+ puts "tmpfile #{tmpfile.path}"
189
+
190
+ puts "content: #{File.read(tmpfile.path)}"
191
+ #yield "-T '#{ tmpfile.path }'"
192
+ yield "#{ tmpfile.path }"
193
+ ensure
194
+
195
+ puts "delete file #{tmpfile.path}"
196
+ tmpfile.delete
197
+ end
198
+
199
+ def paths_to_exclude
200
+ options[:excludes].map {|path|
201
+ "--exclude='#{ prepare_path(path) }'"
202
+ }.join(' ')
203
+ end
204
+
205
+ def prepare_path(path)
206
+
207
+ res = options[:root] ? path : File.expand_path(path)
208
+
209
+ puts "path #{path} ===> #{res}"
210
+
211
+ res
212
+ end
213
+
214
+ def tar_options
215
+ args = options[:tar_options]
216
+ gnu_tar? ? "--ignore-failed-read #{ args }".strip : args
217
+ end
218
+
219
+ def tar_success_codes
220
+ gnu_tar? ? [0, 1] : [0]
221
+ end
222
+
223
+
224
+ ### DSL for RemoteArchive
225
+ class DSL
226
+ def initialize(options)
227
+ @options = options
228
+ end
229
+
230
+
231
+ ### remote server
232
+ def server_host=(val = true)
233
+ @options[:server_host] = val
234
+ end
235
+
236
+ def server_ssh_user=(val = true)
237
+ @options[:server_ssh_user] = val
238
+ end
239
+ def server_ssh_password=(val = true)
240
+ @options[:server_ssh_password] = val
241
+ end
242
+
243
+ ###
244
+ def use_sudo(val = true)
245
+ @options[:sudo] = val
246
+ end
247
+
248
+ def root(path)
249
+ @options[:root] = path
250
+ end
251
+
252
+ def add(path)
253
+ @options[:paths] << path
254
+ end
255
+
256
+ def exclude(path)
257
+ @options[:excludes] << path
258
+ end
259
+
260
+ def tar_options(opts)
261
+ @options[:tar_options] = opts
262
+ end
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,237 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/ssh'
4
+
5
+ require 'sshkit'
6
+ require 'sshkit/dsl'
7
+ require 'sshkit/sudo'
8
+
9
+
10
+ module Backup
11
+ class RemoteData < Archive
12
+ class Error < Backup::Error; end
13
+
14
+ include Utilities::Helpers
15
+ #attr_reader :name, :options
16
+
17
+ include SSHKit::DSL
18
+
19
+ # server options
20
+ attr_accessor :server_host
21
+ attr_accessor :server_ssh_user
22
+ attr_accessor :server_ssh_password
23
+ attr_accessor :server_ssh_key
24
+ attr_accessor :server_path
25
+ attr_accessor :server_command
26
+
27
+
28
+
29
+
30
+
31
+ def initialize(model, name, &block)
32
+ @model = model
33
+ @name = name.to_s
34
+ @options = {
35
+ :sudo => false,
36
+ :root => false,
37
+ :paths => [],
38
+ :excludes => [],
39
+ :tar_options => ''
40
+ }
41
+
42
+ DSL.new(@options).instance_eval(&block)
43
+
44
+ #
45
+ self.server_host = @options[:server_host]
46
+ self.server_ssh_user = @options[:server_ssh_user]
47
+ self.server_ssh_password = @options[:server_ssh_password]
48
+ self.server_path = @options[:server_path]
49
+ self.server_command = @options[:server_command]
50
+ end
51
+
52
+ def perform!
53
+ Logger.info "Creating Archive '#{ name }'..."
54
+
55
+ # local archive
56
+ path = File.join(Config.tmp_path, @model.trigger, 'archives')
57
+ FileUtils.mkdir_p(path)
58
+
59
+ extension = 'tar'
60
+ #temp_archive_file = File.join(path, "#{ name }.#{ extension }")
61
+
62
+ remote = Backup::Remote::Command.new
63
+
64
+
65
+ Dir.mktmpdir do |temp_dir|
66
+ temp_local_file = File.join("#{temp_dir}", File.basename(server_path))
67
+ #temp_local_file = File.join(path, File.basename(server_path))
68
+ #temp_local_file = Tempfile.new("").path+"."+File.extname(server_path)
69
+
70
+ remote_archive_file = server_path
71
+
72
+ # generate backup on remote server
73
+ cmd_remote = server_command
74
+ res_generate = remote.run_ssh_cmd(
75
+ server_host, server_ssh_user, server_ssh_password,
76
+ cmd_remote)
77
+
78
+ if res_generate[:res]==0
79
+ raise 'Cannot create backup on server'
80
+ end
81
+
82
+ # download backup
83
+ res_download = remote.ssh_download_file(
84
+ server_host, server_ssh_user, server_ssh_password,
85
+ remote_archive_file, temp_local_file)
86
+
87
+ if res_download[:res]==0
88
+ raise 'Cannot download file from server'
89
+ end
90
+
91
+ # delete archive on server
92
+ res_delete = remote.run_ssh_cmd(
93
+ server_host, server_ssh_user, server_ssh_password,
94
+ "rm #{remote_archive_file}")
95
+
96
+
97
+ # process archive locally
98
+
99
+ pipeline = Pipeline.new
100
+
101
+ #temp_tar_root= tar_root
102
+ temp_tar_root= temp_dir
103
+ pipeline.add(
104
+ "#{ tar_command } #{ tar_options } -cPf - -C #{temp_tar_root } #{ File.basename(temp_local_file) }",
105
+ tar_success_codes
106
+ )
107
+
108
+ extension = 'tar'
109
+ @model.compressor.compress_with do |command, ext|
110
+ pipeline << command
111
+ extension << ext
112
+ end if @model.compressor
113
+
114
+ pipeline << "#{ utility(:cat) } > '#{ File.join(path, "#{ name }.#{ extension }") }'"
115
+
116
+ #puts "commands: #{pipeline.commands}"
117
+ #exit
118
+
119
+ pipeline.run
120
+
121
+
122
+ if pipeline.success?
123
+ Logger.info "Archive '#{ name }' Complete!"
124
+ else
125
+ raise Error, "Failed to Create Archive '#{ name }'\n" +
126
+ pipeline.error_messages
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def tar_command
135
+ tar = utility(:tar)
136
+ options[:sudo] ? "#{ utility(:sudo) } -n #{ tar }" : tar
137
+ end
138
+
139
+ def tar_root
140
+ options[:root] ? " -C '#{ File.expand_path(options[:root]) }'" : ''
141
+ end
142
+
143
+ def paths_to_package
144
+ options[:paths].map {|path| prepare_path(path) }
145
+ end
146
+
147
+ def with_files_from(paths)
148
+ tmpfile = Tempfile.new('backup-archive-paths')
149
+ paths.each {|path| tmpfile.puts path }
150
+ tmpfile.close
151
+
152
+ puts "tmpfile #{tmpfile.path}"
153
+
154
+ puts "content: #{File.read(tmpfile.path)}"
155
+ #yield "-T '#{ tmpfile.path }'"
156
+ yield "#{ tmpfile.path }"
157
+ ensure
158
+
159
+ puts "delete file #{tmpfile.path}"
160
+ tmpfile.delete
161
+ end
162
+
163
+ def paths_to_exclude
164
+ options[:excludes].map {|path|
165
+ "--exclude='#{ prepare_path(path) }'"
166
+ }.join(' ')
167
+ end
168
+
169
+ def prepare_path(path)
170
+
171
+ res = options[:root] ? path : File.expand_path(path)
172
+
173
+ puts "path #{path} ===> #{res}"
174
+
175
+ res
176
+ end
177
+
178
+ def tar_options
179
+ args = options[:tar_options]
180
+ gnu_tar? ? "--ignore-failed-read #{ args }".strip : args
181
+ end
182
+
183
+ def tar_success_codes
184
+ gnu_tar? ? [0, 1] : [0]
185
+ end
186
+
187
+
188
+ ### DSL for RemoteArchive
189
+ class DSL
190
+ def initialize(options)
191
+ @options = options
192
+ end
193
+
194
+
195
+ ### remote server
196
+ def server_host=(val = true)
197
+ @options[:server_host] = val
198
+ end
199
+
200
+ def server_ssh_user=(val = true)
201
+ @options[:server_ssh_user] = val
202
+ end
203
+ def server_ssh_password=(val = true)
204
+ @options[:server_ssh_password] = val
205
+ end
206
+
207
+ def server_command=(val = true)
208
+ @options[:server_command] = val
209
+ end
210
+ def server_path=(val = true)
211
+ @options[:server_path] = val
212
+ end
213
+
214
+ ###
215
+ def use_sudo(val = true)
216
+ @options[:sudo] = val
217
+ end
218
+
219
+ def root(path)
220
+ @options[:root] = path
221
+ end
222
+
223
+ def add(path)
224
+ @options[:paths] << path
225
+ end
226
+
227
+ def exclude(path)
228
+ @options[:excludes] << path
229
+ end
230
+
231
+ def tar_options(opts)
232
+ @options[:tar_options] = opts
233
+ end
234
+ end
235
+
236
+ end
237
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Backup
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backup-remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Ivak, Michael van Rooijen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-14 00:00:00.000000000 Z
11
+ date: 2016-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: CFPropertyList
@@ -842,14 +842,14 @@ dependencies:
842
842
  requirements:
843
843
  - - '='
844
844
  - !ruby/object:Gem::Version
845
- version: 0.18.1
845
+ version: 0.19.1
846
846
  type: :runtime
847
847
  prerelease: false
848
848
  version_requirements: !ruby/object:Gem::Requirement
849
849
  requirements:
850
850
  - - '='
851
851
  - !ruby/object:Gem::Version
852
- version: 0.18.1
852
+ version: 0.19.1
853
853
  - !ruby/object:Gem::Dependency
854
854
  name: thread_safe
855
855
  requirement: !ruby/object:Gem::Requirement
@@ -1017,6 +1017,8 @@ files:
1017
1017
  - lib/backup/packager.rb
1018
1018
  - lib/backup/pipeline.rb
1019
1019
  - lib/backup/remote/command.rb
1020
+ - lib/backup/remote_archive.rb
1021
+ - lib/backup/remote_data.rb
1020
1022
  - lib/backup/splitter.rb
1021
1023
  - lib/backup/storage/base.rb
1022
1024
  - lib/backup/storage/cloud_files.rb