synco 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +4 -0
  4. data/.simplecov +9 -0
  5. data/.travis.yml +14 -0
  6. data/Gemfile +11 -0
  7. data/README.md +246 -0
  8. data/Rakefile +8 -0
  9. data/bin/synco +30 -0
  10. data/lib/synco.rb +51 -0
  11. data/lib/synco/command.rb +71 -0
  12. data/lib/synco/command/disk.rb +55 -0
  13. data/lib/synco/command/prune.rb +166 -0
  14. data/lib/synco/command/rotate.rb +86 -0
  15. data/lib/synco/command/spawn.rb +39 -0
  16. data/lib/synco/compact_formatter.rb +115 -0
  17. data/lib/synco/controller.rb +119 -0
  18. data/lib/synco/directory.rb +60 -0
  19. data/lib/synco/disk.rb +68 -0
  20. data/lib/synco/method.rb +56 -0
  21. data/lib/synco/methods/rsync.rb +162 -0
  22. data/lib/synco/methods/scp.rb +44 -0
  23. data/lib/synco/methods/zfs.rb +60 -0
  24. data/lib/synco/scope.rb +247 -0
  25. data/lib/synco/script.rb +128 -0
  26. data/lib/synco/server.rb +90 -0
  27. data/lib/synco/shell.rb +44 -0
  28. data/lib/synco/shells/ssh.rb +52 -0
  29. data/lib/synco/version.rb +23 -0
  30. data/media/LSync Logo.artx/Preview/preview.png +0 -0
  31. data/media/LSync Logo.artx/QuickLook/Preview.pdf +0 -0
  32. data/media/LSync Logo.artx/doc.thread +0 -0
  33. data/media/LSync Logo.png +0 -0
  34. data/spec/synco/backup_script.rb +63 -0
  35. data/spec/synco/directory_spec.rb +33 -0
  36. data/spec/synco/local_backup.rb +56 -0
  37. data/spec/synco/local_sync.rb +91 -0
  38. data/spec/synco/method_spec.rb +62 -0
  39. data/spec/synco/rsync_spec.rb +89 -0
  40. data/spec/synco/scp_spec.rb +58 -0
  41. data/spec/synco/script_spec.rb +51 -0
  42. data/spec/synco/shell_spec.rb +42 -0
  43. data/spec/synco/usb_spec.rb +76 -0
  44. data/spec/synco/zfs_spec.rb +50 -0
  45. data/synco.gemspec +35 -0
  46. metadata +254 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fedf0385eb4c7050534dd617e760cd8f103407bd
4
+ data.tar.gz: 510351b706b78342f9c098300fb7fa6ccff59b77
5
+ SHA512:
6
+ metadata.gz: 84434a2dc282d598b3e886c534a20e142c1c015915f3e516a85a065c9e08f5e0abd7e7cd7fa79892bea5f272f0bce8653d2661f3274d8c2f4d1b09477030f614
7
+ data.tar.gz: fba66451cbd62dd7885add0fc05f9dd45d632bb98512777f56e4bfb92f7fd48622b29898a6f0a6660db251775d5e65afcfda3bbd804a345abd2932ce64cf7c82
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --warnings
@@ -0,0 +1,9 @@
1
+
2
+ SimpleCov.start do
3
+ add_filter "/spec/"
4
+ end
5
+
6
+ if ENV['TRAVIS']
7
+ require 'coveralls'
8
+ Coveralls.wear!
9
+ end
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 2.1.8
5
+ - 2.2.4
6
+ - 2.3.0
7
+ - ruby-head
8
+ - rbx-2
9
+ env: COVERAGE=true
10
+ matrix:
11
+ fast_finish: true
12
+ allow_failures:
13
+ - rvm: "ruby-head"
14
+ - rvm: "rbx-2"
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'coveralls', require: false
9
+
10
+ gem 'fssm'
11
+ end
@@ -0,0 +1,246 @@
1
+ # Synco
2
+
3
+ Synco is a tool for scripted synchronization and backups. It provides a custom Ruby DSL for describing backup and synchronization tasks involving one more more system and disk. It is designed to provide flexibility while reducing the complexity multi-server backups.
4
+
5
+ * Single and multi-server data synchronization.
6
+ * Incremental backups both locally and remotely.
7
+ * Backup staging and coordination.
8
+ * Backup verification using [Fingerprint](https://github.com/ioquatix/fingerprint).
9
+ * Data backup redundancy controlled via DNS.
10
+
11
+ [![Build Status](https://secure.travis-ci.org/ioquatix/synco.svg)](http://travis-ci.org/ioquatix/synco)
12
+ [![Code Climate](https://codeclimate.com/github/ioquatix/synco.svg)](https://codeclimate.com/github/ioquatix/synco)
13
+ [![Coverage Status](https://coveralls.io/repos/ioquatix/synco/badge.svg)](https://coveralls.io/r/ioquatix/synco)
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'synco'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install synco
28
+
29
+ ## Usage
30
+
31
+ Synco imposes a particular structure regarding the organisation of backup scripts: Backup scripts involve a set of servers and directories. A server is a logical unit where files are available or stored. Directories are relative paths which are resolved relative to a server's root path.
32
+
33
+ A simple backup script might look something like this:
34
+
35
+ #!/usr/bin/env ruby
36
+
37
+ require 'synco'
38
+ require 'synco/methods/rsync'
39
+
40
+ Synco::run_script do |script|
41
+ script.method = Synco::Methods::RSync.new(archive: true)
42
+
43
+ server(:master) do |server|
44
+ server.host = "server.example.com"
45
+ server.root = "/"
46
+ end
47
+
48
+ server(:backup) do |server|
49
+ server.host = "backup.example.com"
50
+ server.root = "/tank/backups/server.example.com"
51
+ end
52
+
53
+ backup('etc', 'var', 'srv', 'home',
54
+ arguments: %W{--exclude cache/ --exclude tmp/}
55
+ )
56
+ end
57
+
58
+ This will produce an identical copy using rsync of the specified directories. As an example `server.example.com:/etc` would be copied to `backup.example.com:/tank/backups/server.example.com/etc`.
59
+
60
+ ### RSync Snapshots
61
+
62
+ Building on the above backup, you can use `Synco::Methods::RSyncSnapshot` which supports snapshot based backups. It creates a snapshot into a sub-directory called `latest.snapshot` and uses RSync's `--link-dest` to hard-link files when unchanged. Synco provides scripts to rotate and prune these backups as required, but you must invoke them as part of the script:
63
+
64
+ server(:backup) do |server|
65
+ server.host = "backup.example.com"
66
+ server.root = "/"
67
+
68
+ server.on(:success) do
69
+ run "synco", "rotate", chdir: target_server.root
70
+ run "synco", "prune", chdir: target_server.root
71
+ end
72
+ end
73
+
74
+ These commands can also be run from the command line.
75
+
76
+ rotate [--format <name>] [--latest <name>] [--snapshot <name>]
77
+ Rotate a backup snapshot into a timestamped directory.
78
+
79
+ [--format <name>] Set the name of the backup rotations, including strftime expansions. Default: %Y.%m.%d-%H.%M.%S
80
+ [--latest <name>] The name of the latest backup symlink. Default: latest
81
+ [--snapshot <name>] The name of the in-progress backup snapshot. Default: latest.snapshot
82
+
83
+ prune [--hourly <count>] [--daily <count>] [--weekly <count>] [--monthly <count>] [--quarterly <count>] [--yearly <count>] [--format <name>] [--latest <name>] [--keep <new|old>] [--dry]
84
+ Prune old backups to reduce disk usage according to a given policy.
85
+
86
+ [--hourly <count>] Set the number of hourly backups to keep. Default: 24
87
+ [--daily <count>] Set the number of daily backups to keep. Default: 28
88
+ [--weekly <count>] Set the number of weekly backups to keep. Default: 52
89
+ [--monthly <count>] Set the number of monthly backups to keep. Default: 36
90
+ [--quarterly <count>] Set the number of quaterly backups to keep. Default: 40
91
+ [--yearly <count>] Set the number of yearly backups to keep. Default: 20
92
+ [--format <name>] Set the name of the backup rotations, including strftime expansions. Default: %Y.%m.%d-%H.%M.%S
93
+ [--latest <name>] The name of the latest backup symlink. Default: latest
94
+ [--keep <new|old>] Keep the younger or older backups within the same period division Default: old
95
+ [--dry] Print out what would be done rather than doing it.
96
+
97
+ ### Mounting Disks
98
+
99
+ Synco supports mounting disks before the backup begins and unmounting them after done. The specifics of this process may require some adjustment based on your OS. For example on linux, `sudo` is used to invoke `mount` and `umount`.
100
+
101
+ server(:destination) do |server|
102
+ self.mountpoint = '/mnt/backups'
103
+ self.root = File.join(server.mountpoint, 'laptop')
104
+
105
+ server.on(:prepare) do
106
+ # synco mount uses labels, e.g. the disk partition has LABEL=backups
107
+ target_server.run "synco", "mount", target_server.mountpoint, 'backups'
108
+ end
109
+
110
+ server.on(:finish) do
111
+ target_server.run "synco", "unmount", target_server.mountpoint
112
+ end
113
+ end
114
+
115
+ On Linux, you might want to create the file `/etc/sudoers.d/synco` with the following contents:
116
+
117
+ %wheel ALL=(root) NOPASSWD: /bin/mount
118
+ %wheel ALL=(root) NOPASSWD: /bin/umount
119
+
120
+ Please make sure you take the time to educate yourself on the security of such a setup.
121
+
122
+ ### Database Backups
123
+
124
+ If you'd like to dump data before running the backup, it's possible using the event handling mechanisms:
125
+
126
+ server(:master) do |server|
127
+ server.host = "server.example.com"
128
+ server.root = "/"
129
+
130
+ server.on(:prepare) do
131
+ # Dump MySQL to /srv/mysql
132
+ run '/etc/lsync/mysql-backup.sh'
133
+ end
134
+ end
135
+
136
+ The exact contents of `mysql-backup.sh` will depend on your requirements, but here is an example:
137
+
138
+ #!/usr/bin/env bash
139
+
140
+ BACKUP_DIR=/srv/mysql
141
+ MYSQL=/usr/bin/mysql
142
+ MYSQLDUMP=/usr/bin/mysqldump
143
+
144
+ databases=`mysql --user=backup -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|_test|_restore)"`
145
+
146
+ # http://stackoverflow.com/questions/451404/how-to-obtain-a-correct-dump-using-mysqldump-and-single-transaction-when-ddl-is
147
+ MYSQLDUMP_OPTIONS="--force --skip-opt --single-transaction --add-drop-table --create-options --quick --extended-insert --set-charset --disable-keys"
148
+
149
+ for db in $databases; do
150
+ echo "Dumping database $db to $BACKUP_DIR/$db.sql.xz..."
151
+ mysqldump --user=backup $MYSQLDUMP_OPTIONS --databases $db | xz > "$BACKUP_DIR/$db.sql.xz"
152
+ done
153
+
154
+ ### Fingerprint Integration
155
+
156
+ It is possible to make a [cryptographic checksum of the data](https://github.com/ioquatix/fingerprint). On a filesystem that support immutable snapshots, you can do this before the data is copied. For traditional filesystems, you generally need to do this afterwards.
157
+
158
+ server(:master) do |server|
159
+ server.host = "server.example.com"
160
+ server.root = "/"
161
+
162
+ server.on(:success) do
163
+ # Run fingerprint on the backup data:
164
+ run 'fingerprint', '--root', target_server.root, 'analyze'
165
+ end
166
+ end
167
+
168
+ Fingerprint is used in many of the specs to verify file copies.
169
+
170
+ ### ZFS Snapshots
171
+
172
+ *This part of Synco is still under heavy development*
173
+
174
+ Synco can manage synchronization and backups of ZFS partitions. However, to use the standard tools, it is necessary to enable `zfs_admin_snapshot`, in `/etc/modprobe.d/zfs.conf`:
175
+
176
+ options zfs zfs_admin_snapshot=1
177
+
178
+ Propagate user permissions for the ZFS partition:
179
+
180
+ sudo zfs allow -ld -u `whoami` create,mount,send,receive,snapshot tank/test
181
+
182
+ ### Backup staging
183
+
184
+ Synco in a previous life supported backup staging. However, at this time it's not available except in a very limited form: backup scripts which use `Synco.run_script` use explicit locking so that it's not possible to run the same backup at the same time. In the future, staging sequential and parallel backups will be added.
185
+
186
+ ### DNS Failover
187
+
188
+ **This behaviour is not well tested**
189
+
190
+ Synco uses DNS to resolve the master server. This allows for bi-directional synchronization and other interesting setups.
191
+
192
+ Firstly, a backup script defaults to the server with the name `:master` as the master, where data is replicated FROM.
193
+
194
+ However, it is possible instead to specify a hostname, e.g. `primary.example.com`. Then, specify several servers, e.g. `s01.example.com`, `s02.example.com` and so on:
195
+
196
+ Synco::run_script do |script|
197
+ script.method = Synco::Methods::RSync.new(archive: true)
198
+
199
+ script.master = "primary.example.com"
200
+
201
+ server("s01.example.com") do |server|
202
+ server.root = "/"
203
+ end
204
+
205
+ server("s02.example.com") do |server|
206
+ server.root = "/"
207
+ end
208
+
209
+ backup('srv/http',
210
+ arguments: %W{--exclude cache/ --exclude tmp/}
211
+ )
212
+ end
213
+
214
+ When you run the script, the behaviour will depend on whether `primary.example.com` points to `s01.example.com` or `s02.example.com`. The data will always be copied from the master server to the other servers.
215
+
216
+ ## Contributing
217
+
218
+ 1. Fork it
219
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
220
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
221
+ 4. Push to the branch (`git push origin my-new-feature`)
222
+ 5. Create new Pull Request
223
+
224
+ ## License
225
+
226
+ Released under the MIT license.
227
+
228
+ Copyright, 2016, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
229
+
230
+ Permission is hereby granted, free of charge, to any person obtaining a copy
231
+ of this software and associated documentation files (the "Software"), to deal
232
+ in the Software without restriction, including without limitation the rights
233
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
234
+ copies of the Software, and to permit persons to whom the Software is
235
+ furnished to do so, subject to the following conditions:
236
+
237
+ The above copyright notice and this permission notice shall be included in
238
+ all copies or substantial portions of the Software.
239
+
240
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
241
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
242
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
243
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
244
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
245
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
246
+ THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ["--require", "simplecov"] if ENV['COVERAGE']
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ # This script takes a given path, and renames it with the given format.
24
+ # It then ensures that there is a symlink called "latest" that points
25
+ # to the renamed directory.
26
+
27
+ require 'synco/command'
28
+
29
+ application = Synco::Command::Top.new(ARGV)
30
+ application.invoke
@@ -0,0 +1,51 @@
1
+ # Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'yaml'
22
+ require 'socket'
23
+ require 'set'
24
+ require 'logger'
25
+
26
+ require_relative 'synco/version'
27
+ require_relative 'synco/script'
28
+ require_relative 'synco/scope'
29
+
30
+ require 'fileutils'
31
+ require 'optparse'
32
+
33
+ require 'lockfile'
34
+
35
+ module Synco
36
+ # Run a prepared backup script using a lockfile.
37
+ def self.run_script(*arguments, **options, &block)
38
+ script = Synco::Script.build(*arguments, **options, &block)
39
+ lockfile_path = $0 + ".lock"
40
+
41
+ # script.on(:failure) do |exception|
42
+ # logger.error{exception}
43
+ #
44
+ # raise
45
+ # end
46
+
47
+ Lockfile.new(lockfile_path, :retries => 0) do
48
+ Runner.new(script).call
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ # This script takes a given path, and renames it with the given format.
22
+ # It then ensures that there is a symlink called "latest" that points
23
+ # to the renamed directory.
24
+
25
+ require 'samovar'
26
+
27
+ require_relative 'command/spawn'
28
+ require_relative 'command/rotate'
29
+ require_relative 'command/prune'
30
+ require_relative 'command/disk'
31
+
32
+ module Synco
33
+ module Command
34
+ class Top < Samovar::Command
35
+ self.description = "A backup and synchronizatio tool."
36
+
37
+ options do
38
+ option '--root <path>', "Work in the given root directory."
39
+ option '-h/--help', "Print out help information."
40
+ option '-v/--version', "Print out the application version."
41
+ end
42
+
43
+ def chdir(&block)
44
+ if root = @options[:root]
45
+ Dir.chdir(root, &block)
46
+ else
47
+ yield
48
+ end
49
+ end
50
+
51
+ nested '<command>',
52
+ 'spawn' => Spawn,
53
+ 'rotate' => Rotate,
54
+ 'prune' => Prune,
55
+ 'mount' => Mount,
56
+ 'unmount' => Unmount
57
+
58
+ def invoke(program_name: File.basename($0))
59
+ if @options[:version]
60
+ puts "synco v#{Synco::VERSION}"
61
+ elsif @options[:help] or @command.nil?
62
+ print_usage(program_name)
63
+ else
64
+ chdir do
65
+ @command.invoke(self)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end