yolo_backup 0.0.0

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.
@@ -0,0 +1 @@
1
+ yolo-backup
@@ -0,0 +1 @@
1
+ 1.9.3-p194
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ yolo_backup (0.0.0)
5
+ activesupport (~> 3.2)
6
+ sys-filesystem (~> 1.1)
7
+ thor (~> 0.18)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (3.2.13)
13
+ i18n (= 0.6.1)
14
+ multi_json (~> 1.0)
15
+ diff-lcs (1.2.4)
16
+ ffi (1.8.1)
17
+ i18n (0.6.1)
18
+ multi_json (1.7.3)
19
+ rake (10.0.4)
20
+ rspec (2.13.0)
21
+ rspec-core (~> 2.13.0)
22
+ rspec-expectations (~> 2.13.0)
23
+ rspec-mocks (~> 2.13.0)
24
+ rspec-core (2.13.1)
25
+ rspec-expectations (2.13.0)
26
+ diff-lcs (>= 1.1.3, < 2.0)
27
+ rspec-mocks (2.13.1)
28
+ simplecov (0.7.1)
29
+ multi_json (~> 1.0)
30
+ simplecov-html (~> 0.7.1)
31
+ simplecov-html (0.7.1)
32
+ sys-filesystem (1.1.0)
33
+ ffi
34
+ thor (0.18.1)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ rake (~> 10.0)
41
+ rspec (~> 2.12)
42
+ simplecov (~> 0.7)
43
+ yolo_backup!
@@ -0,0 +1,11 @@
1
+ # yolo_backup
2
+
3
+ yolo_backup allows you to create incremental backups of multiple servers using rsync over SSH. You can specify which backups to keep (daily, weekly, monthly, …) on a per-server basis.
4
+
5
+ ## Setup
6
+
7
+ ### Configuration
8
+
9
+ yolo_backup automatically loads `/etc/yolo_backup.yml`. If you would like to use a different path, use the `--config` parameter.
10
+
11
+ Take a look at the [example configuration](docs/configuration_example.yml) to see what's possible.
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ task :default => :spec
7
+
8
+ desc 'Run all specs'
9
+ RSpec::Core::RakeTask.new(:spec) do |spec|
10
+ spec.pattern = FileList['spec/**/*_spec.rb']
11
+ end
@@ -0,0 +1,47 @@
1
+ storages:
2
+ default:
3
+ path: "/var/backups/%{hostname}"
4
+ external:
5
+ path: "/mnt/external/backups/%{hostname}"
6
+
7
+ rotations:
8
+ default:
9
+ daily: 10
10
+ monthly: 12
11
+ yearly: 3
12
+ extended:
13
+ hourly: 24
14
+ daily: 10
15
+ monthly: 12
16
+ yearly: 5
17
+
18
+ exclude_sets:
19
+ # be sure to define a default exclude set!
20
+ default:
21
+ - '/dev/*'
22
+ - '/proc/*'
23
+ - '/sys/*'
24
+ - '/tmp/*'
25
+ - '/run/*'
26
+ - '/mnt/*'
27
+ - '/media/*'
28
+ - '/lost+found'
29
+ alternative:
30
+ - '/dev/*'
31
+ - '/proc/*'
32
+ - '/sys/*'
33
+ - '/run/*'
34
+ - '/lost+found'
35
+ - '/var/log/*'
36
+
37
+ servers:
38
+ peter:
39
+ ssh_port: 2222
40
+ rotation: extended
41
+ excludes: # additional excludes
42
+ - '/home/dont_backup_this_user/'
43
+ nasibisibusi:
44
+ ssh_host: 'nasi.bisi.busi.example.com'
45
+ ssh_key: '/home/example/.ssh/id_dsa'
46
+ storage: external
47
+ exclude_set: alternative
@@ -0,0 +1,6 @@
1
+ require 'active_support/all'
2
+
3
+ require 'yolo_backup/version'
4
+
5
+ module YOLOBackup
6
+ end
@@ -0,0 +1,39 @@
1
+ require 'yolo_backup/backup_runner/job'
2
+ require 'yolo_backup/helper/log'
3
+
4
+ module YOLOBackup
5
+ class BackupRunner
6
+ include Helper::Log
7
+
8
+ class Error < StandardError; end
9
+ class UnknownServerError < Error; end
10
+
11
+ OPTIONS = %w{servers verbose}
12
+
13
+ OPTIONS.each do |option|
14
+ attr_accessor option
15
+ end
16
+
17
+ alias_method :verbose?, :verbose
18
+
19
+ def initialize(options)
20
+ OPTIONS.each do |option|
21
+ send("#{option}=", options[option]) if options[option]
22
+ end
23
+ end
24
+
25
+ def backup(server_name = nil)
26
+ if server_name.nil?
27
+ servers.keys.each do |server_name|
28
+ backup(server_name)
29
+ end
30
+ else
31
+ raise UnknownServerError, "Server #{server_name} not defined" unless servers.key?(server_name)
32
+ server = servers[server_name]
33
+ log "Backup of #{server} requested" if verbose?
34
+ job = Job.new 'server' => server, 'verbose' => verbose?
35
+ job.start
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,6 @@
1
+ module YOLOBackup
2
+ class BackupRunner
3
+ class Backend
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,31 @@
1
+ require 'yolo_backup/backup_runner/backend'
2
+
3
+ class YOLOBackup::BackupRunner::Backend::Rsync < YOLOBackup::BackupRunner::Backend
4
+ class Error < StandardError; end
5
+
6
+ DEFAULT_RSYNC_OPTIONS = '-aAX --numeric-ids'
7
+
8
+ attr_reader :server
9
+
10
+ def initialize(server)
11
+ @server = server
12
+ end
13
+
14
+ def start_backup
15
+ server.storage.initiate_backup(server) do |path|
16
+ options = [DEFAULT_RSYNC_OPTIONS]
17
+ if server.latest_backup
18
+ latest_backup = File.expand_path("#{path}/../#{server.latest_backup}")
19
+ options << "--link-dest='#{latest_backup}'"
20
+ end
21
+ ssh_options = []
22
+ ssh_options << "-i '#{server.ssh_key}'" unless server.ssh_key.nil?
23
+ ssh_options << "-p '#{server.ssh_port}'" unless server.ssh_port.nil?
24
+ options << "-e \"ssh #{ssh_options.join(' ')}\""
25
+ command = "rsync #{options.join(' ')} #{server.ssh_user || 'root'}@#{server.ssh_host || server}:/ '#{path}'"
26
+ puts command
27
+ output = `#{command}`
28
+ raise Error, "rsync command failed: #{$?}" unless $?.success?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ require 'yolo_backup/helper/log'
2
+ require 'yolo_backup/backup_runner/backend/rsync'
3
+
4
+ module YOLOBackup
5
+ class BackupRunner
6
+ class Job
7
+ include Helper::Log
8
+
9
+ OPTIONS = %w{server verbose}
10
+
11
+ OPTIONS.each do |option|
12
+ attr_accessor option
13
+ end
14
+
15
+ alias_method :verbose?, :verbose
16
+
17
+ def initialize(options)
18
+ OPTIONS.each do |option|
19
+ send("#{option}=", options[option]) if options[option]
20
+ end
21
+ end
22
+
23
+ def start
24
+ if backup_required?
25
+ log "Latest backup (#{server.latest_backup}) is older than maximum backup age (#{maximum_backup_age})" if verbose?
26
+ log "Starting backup of #{server}"
27
+ backend.start_backup
28
+ log "Backup completed" if verbose?
29
+ log "Cleaning up old backups" if verbose?
30
+ log "Deleted #{server.cleanup_backups.length} backups" if verbose?
31
+ else
32
+ log "Backup not required (latest backup = #{server.latest_backup}, maximum backup age = #{maximum_backup_age})" if verbose?
33
+ end
34
+ end
35
+
36
+ def backup_required?
37
+ latest_backup = server.latest_backup
38
+ return latest_backup.nil? || latest_backup < maximum_backup_age
39
+ end
40
+
41
+ def maximum_backup_age
42
+ case server.rotation.minimum_unit
43
+ when 'hourly'
44
+ 1.hour.ago
45
+ when 'daily'
46
+ 1.day.ago
47
+ when 'weekly'
48
+ 1.week.ago
49
+ when 'monthly'
50
+ 1.month.ago
51
+ when 'yearly'
52
+ 1.year.ago
53
+ end
54
+ end
55
+
56
+ def backend
57
+ @backend ||= YOLOBackup::BackupRunner::Backend::Rsync.new(server)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,111 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+
4
+ require 'yolo_backup/storage_pool/file'
5
+ require 'yolo_backup/rotation_plan'
6
+ require 'yolo_backup/backup_runner'
7
+ require 'yolo_backup/server'
8
+ require 'yolo_backup/exclude_set'
9
+
10
+ module YOLOBackup
11
+ class CLI < Thor
12
+ DEFAULT_CONFIG_FILE = '/etc/yolo_backup.yml'
13
+ DEFAULT_STORAGE_POOL_NAME = 'default'
14
+ DEFAULT_ROTATION_PLAN_NAME = 'default'
15
+ DEFAULT_EXCLUDE_SET_NAME = 'default'
16
+
17
+ class_option :verbose, :type => :boolean
18
+ class_option :config, :type => :string, :banner => "path to config file (default #{DEFAULT_CONFIG_FILE})"
19
+
20
+ desc 'status', 'Display backup status and statistics'
21
+ def status(storage = nil)
22
+ storage_pools.values.each do |storage_pool|
23
+ puts "#{storage_pool}:"
24
+ if storage_pool.ready?
25
+ storage_pool.statistics.tap do |stats|
26
+ puts " Capacity: #{'%4d' % (stats[:capacity] / 1000 / 1000 / 1000)} GB"
27
+ puts " Available: #{'%4d' % (stats[:available] / 1000 / 1000 / 1000)} GB"
28
+ end
29
+ else
30
+ puts " STORAGE NOT READY" unless storage_pool.ready?
31
+ end
32
+ end
33
+ end
34
+
35
+ desc 'backup [SERVER]', 'Start backup process'
36
+ def backup(server = nil)
37
+ backup_runner = BackupRunner.new('servers' => servers, 'verbose' => verbose?)
38
+ backup_runner.backup(server)
39
+ end
40
+
41
+ private
42
+ def verbose?
43
+ !!options[:verbose]
44
+ end
45
+
46
+ def config
47
+ @config ||= YAML::load(File.open(options[:config] || DEFAULT_CONFIG_FILE))
48
+ end
49
+
50
+ def storage_pools
51
+ return @storage_pools unless @storage_pools.nil?
52
+
53
+ (@storage_pools = {}).tap do |storage_pools|
54
+ config['storages'].each do |name, options|
55
+ storage_pools[name] = StoragePool::File.new(name, options)
56
+ end
57
+ end
58
+ end
59
+
60
+ def rotation_plans
61
+ return @rotation_plans unless @rotation_plans.nil?
62
+
63
+ (@rotation_plans = {}).tap do |rotation_plans|
64
+ config['rotations'].each do |name, options|
65
+ rotation_plans[name] = RotationPlan.new(name, options)
66
+ end
67
+ end
68
+ end
69
+
70
+ def exclude_sets
71
+ return @exclude_sets unless @exclude_sets.nil?
72
+
73
+ (@exclude_sets = {}).tap do |exclude_sets|
74
+ config['exclude_sets'].each do |name, options|
75
+ exclude_sets[name] = ExcludeSet.new(name, options)
76
+ end
77
+ end
78
+ end
79
+
80
+ def servers
81
+ return @servers unless @servers.nil?
82
+
83
+ (@servers = {}).tap do |servers|
84
+ config['servers'].each do |name, options|
85
+ options['storage'] = storage_by_name(options['storage'])
86
+ options['rotation'] = rotation_by_name(options['rotation'])
87
+ options['excludes'] = (options['excludes'] || []) + exlude_set_by_name(options['exclude_set']).excludes
88
+ servers[name] = Server.new(name, options)
89
+ end
90
+ end
91
+ end
92
+
93
+ def storage_by_name(name = nil)
94
+ name ||= DEFAULT_STORAGE_POOL_NAME
95
+ raise "Storage #{name} not defined" unless storage_pools.key?(name)
96
+ storage_pools[name]
97
+ end
98
+
99
+ def rotation_by_name(name = nil)
100
+ name ||= DEFAULT_ROTATION_PLAN_NAME
101
+ raise "Rotation plan #{name} not defined" unless rotation_plans.key?(name)
102
+ rotation_plans[name]
103
+ end
104
+
105
+ def exlude_set_by_name(name = nil)
106
+ name ||= DEFAULT_EXCLUDE_SET_NAME
107
+ raise "Exclude set #{name} not defined" unless exclude_sets.key?(name)
108
+ exclude_sets[name]
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,21 @@
1
+ class String
2
+ INTERPOLATION_PATTERN = Regexp.union(
3
+ /%%/,
4
+ /%\{(\w+)\}/
5
+ )
6
+ def interpolate(values)
7
+ self.gsub(INTERPOLATION_PATTERN) do |match|
8
+ if match == '%%'
9
+ '%'
10
+ else
11
+ key = ($1 || $2).to_sym
12
+ unless values.key?(key)
13
+ raise "Mission interpolation argument: #{key}"
14
+ end
15
+ value = values[key]
16
+ value = value.call(values) if value.respond_to?(:call)
17
+ $3 ? sprintf("%#{$3}", value) : value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module YOLOBackup
2
+ class ExcludeSet
3
+ attr_reader :name, :excludes
4
+
5
+ def initialize(name, excludes)
6
+ @name = name
7
+ @excludes = excludes
8
+ end
9
+
10
+ def to_s
11
+ "#{name} (#{excludes.join(', ')})"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module YOLOBackup
2
+ module Helper
3
+ module Log
4
+ def log(msg)
5
+ puts "#{Time.now.iso8601} [#{self.class.name}] #{msg}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module YOLOBackup
2
+ class RotationPlan
3
+ SCHEDULE_OPTIONS = %w{ hourly daily weekly monthly yearly }
4
+
5
+ SCHEDULE_OPTIONS.each do |option|
6
+ attr_accessor option
7
+ end
8
+
9
+ attr_reader :name
10
+
11
+ def initialize(name, options)
12
+ @name = name
13
+ SCHEDULE_OPTIONS.each do |option|
14
+ send("#{option}=", options[option]) if options.key?(option)
15
+ end
16
+ end
17
+
18
+ def minimum_unit
19
+ SCHEDULE_OPTIONS.each do |option|
20
+ return option if send(option).to_i > 0
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module YOLOBackup
2
+ class Server
3
+ OPTIONS = %w{ excludes rotation ssh_host ssh_key ssh_port ssh_user storage }
4
+
5
+ OPTIONS.each do |option|
6
+ attr_accessor option
7
+ end
8
+
9
+ attr_reader :name
10
+
11
+ def initialize(name, options)
12
+ @name = name
13
+ OPTIONS.each do |option|
14
+ send("#{option}=", options[option]) if options.key?(option)
15
+ end
16
+ end
17
+
18
+ def latest_backup
19
+ storage.latest_backup(self)
20
+ end
21
+
22
+ def cleanup_backups
23
+ storage.cleanup(self)
24
+ end
25
+
26
+ def to_s
27
+ name
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module YOLOBackup
2
+ class StoragePool
3
+ attr_reader :name
4
+
5
+ def initialize(name, options=nil)
6
+ @name = name
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,70 @@
1
+ require 'time'
2
+ require 'sys/filesystem'
3
+
4
+ require 'yolo_backup/storage_pool'
5
+ require 'yolo_backup/core_ext/string'
6
+
7
+ class YOLOBackup::StoragePool::File < YOLOBackup::StoragePool
8
+ require 'yolo_backup/storage_pool/file/cleaner'
9
+
10
+ OPTIONS = %w{ path }
11
+
12
+ attr_accessor :path
13
+
14
+ def initialize(name, options = nil)
15
+ super
16
+ OPTIONS.each do |key|
17
+ send("#{key}=", options[key]) if options[key]
18
+ end
19
+ end
20
+
21
+ def to_s
22
+ "#{name} (#{wildcard_path})"
23
+ end
24
+
25
+ def statistics
26
+ stats = Sys::Filesystem.stat(base_path)
27
+ {
28
+ capacity: stats.blocks * stats.block_size,
29
+ available: stats.blocks_available * stats.block_size
30
+ }
31
+ end
32
+
33
+ def ready?
34
+ ::File.directory?(base_path) && ::File.readable?(base_path)
35
+ end
36
+
37
+ def latest_backup(server)
38
+ server_path = server_path(server)
39
+ return nil unless ::File.directory?(server_path) && ::File.symlink?("#{server_path}/latest") && ::File.directory?("#{server_path}/latest")
40
+ target = ::File.basename(::File.readlink("#{server_path}/latest"))
41
+ Time.parse(target)
42
+ end
43
+
44
+ def initiate_backup(server, &block)
45
+ server_path = server_path(server)
46
+ path = "#{server_path}/#{Time.now.iso8601}/"
47
+ yield(path)
48
+ latest_path = "#{server_path}/latest"
49
+ ::File.unlink(latest_path) if ::File.symlink?(latest_path)
50
+ ::File.symlink(path, latest_path)
51
+ end
52
+
53
+ def cleanup(server)
54
+ cleaner = Cleaner.new(self, server)
55
+ cleaner.cleanup
56
+ end
57
+
58
+ private
59
+ def wildcard_path
60
+ path.interpolate :hostname => '*'
61
+ end
62
+
63
+ def base_path
64
+ path.interpolate(:hostname => '')
65
+ end
66
+
67
+ def server_path(hostname)
68
+ path.interpolate(:hostname => hostname)
69
+ end
70
+ end
@@ -0,0 +1,71 @@
1
+ require 'fileutils'
2
+
3
+ require 'yolo_backup/storage_pool/file'
4
+
5
+ class YOLOBackup::StoragePool::File::Cleaner
6
+ attr_reader :storage_pool, :server
7
+
8
+ def initialize(storage_pool, server)
9
+ @storage_pool = storage_pool
10
+ @server = server
11
+ end
12
+
13
+ def cleanup
14
+ unused_backups = all_backups - required_backups
15
+ unused_backups.each do |unused_backup|
16
+ path = "#{server_path}/#{unused_backup.iso8601}"
17
+ p path
18
+ FileUtils.rm_r path, :force => true
19
+ end
20
+ unused_backups
21
+ end
22
+
23
+ private
24
+ def required_backups
25
+ required_backups = {}
26
+ all_backups.each do |date|
27
+ {
28
+ :year => :yearly,
29
+ :month => :monthly,
30
+ :week => :weekly,
31
+ :day => :daily,
32
+ :hour => :hourly
33
+ }.each do |accuracy, schedule_option_name|
34
+ keep_count = server.rotation.send("#{schedule_option_name}").to_i
35
+ next if keep_count.nil? || keep_count.zero?
36
+ if required_backups[accuracy].nil?
37
+ required_backups[accuracy] = [date]
38
+ elsif !same_date(date, required_backups[accuracy].last, accuracy)
39
+ required_backups[accuracy] << date
40
+ end
41
+ if required_backups[accuracy].length > keep_count
42
+ required_backups[accuracy].shift
43
+ end
44
+ end
45
+ end
46
+ required_backups = required_backups.values.flatten
47
+ required_backups << server.latest_backup
48
+ required_backups.uniq
49
+ end
50
+
51
+ def all_backups
52
+ backups = []
53
+ Dir.chdir(server_path) do
54
+ Dir.entries('.').sort.each do |file|
55
+ next if ['.', '..'].include?(file)
56
+ next unless ::File.directory?(file) && !::File.symlink?(file)
57
+ backups << Time.parse(file)
58
+ end
59
+ end
60
+ backups
61
+ end
62
+
63
+ def server_path
64
+ storage_pool.path.interpolate(:hostname => server)
65
+ end
66
+
67
+ def same_date(time1, time2, accuracy)
68
+ end_of = "end_of_#{accuracy}"
69
+ time1.send(end_of) == time2.send(end_of)
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module YOLOBackup
2
+ VERSION = '0.0.0'
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'yolo_backup/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'yolo_backup'
7
+ s.version = YOLOBackup::VERSION
8
+ s.authors = ['Nils Caspar']
9
+ s.email = 'ncaspar@me.com'
10
+ s.homepage = 'http://github.org/pencil/yolo_backup'
11
+ s.license = 'MIT'
12
+ s.summary = 'A simple Ruby script to create incremental backups of multiple servers using rsync over SSH.'
13
+ s.description = 'yolo_backup allows you to create incremental backups of multiple servers using rsync over SSH. You can specify which backups to keep (daily, weekly, monthly, …) on a per-server basis.'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ['lib']
19
+
20
+ s.add_development_dependency 'rake', '~> 10.0'
21
+ s.add_development_dependency 'rspec', '~> 2.12'
22
+ s.add_development_dependency 'simplecov', '~> 0.7'
23
+
24
+ s.add_dependency 'thor', '~> 0.18'
25
+ s.add_dependency 'sys-filesystem', '~> 1.1'
26
+ s.add_dependency 'activesupport', '~> 3.2'
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yolo_backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nils Caspar
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '10.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '10.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '2.12'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.12'
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.7'
62
+ - !ruby/object:Gem::Dependency
63
+ name: thor
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0.18'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.18'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sys-filesystem
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.1'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.1'
94
+ - !ruby/object:Gem::Dependency
95
+ name: activesupport
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '3.2'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '3.2'
110
+ description: yolo_backup allows you to create incremental backups of multiple servers
111
+ using rsync over SSH. You can specify which backups to keep (daily, weekly, monthly,
112
+ …) on a per-server basis.
113
+ email: ncaspar@me.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .ruby-gemset
119
+ - .ruby-version
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - README.md
123
+ - Rakefile
124
+ - docs/configuration_example.yml
125
+ - lib/yolo_backup.rb
126
+ - lib/yolo_backup/backup_runner.rb
127
+ - lib/yolo_backup/backup_runner/backend.rb
128
+ - lib/yolo_backup/backup_runner/backend/rsync.rb
129
+ - lib/yolo_backup/backup_runner/job.rb
130
+ - lib/yolo_backup/cli.rb
131
+ - lib/yolo_backup/core_ext/string.rb
132
+ - lib/yolo_backup/exclude_set.rb
133
+ - lib/yolo_backup/helper/log.rb
134
+ - lib/yolo_backup/rotation_plan.rb
135
+ - lib/yolo_backup/server.rb
136
+ - lib/yolo_backup/storage_pool.rb
137
+ - lib/yolo_backup/storage_pool/file.rb
138
+ - lib/yolo_backup/storage_pool/file/cleaner.rb
139
+ - lib/yolo_backup/version.rb
140
+ - yolo_backup.gemspec
141
+ homepage: http://github.org/pencil/yolo_backup
142
+ licenses:
143
+ - MIT
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ! '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubyforge_project:
162
+ rubygems_version: 1.8.23
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: A simple Ruby script to create incremental backups of multiple servers using
166
+ rsync over SSH.
167
+ test_files: []