easy_backup 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/easy_backup.gemspec +31 -0
- data/lib/easy_backup/adapter/db/postgre_sql.rb +81 -0
- data/lib/easy_backup/adapter/file_system.rb +83 -0
- data/lib/easy_backup/adapter/sftp.rb +36 -0
- data/lib/easy_backup/base.rb +46 -0
- data/lib/easy_backup/configuration.rb +27 -0
- data/lib/easy_backup/extension/net_sftp_session.rb +13 -0
- data/lib/easy_backup/frequency.rb +13 -0
- data/lib/easy_backup/runner.rb +9 -0
- data/lib/easy_backup/version.rb +3 -0
- data/lib/easy_backup.rb +59 -0
- data/spec/config.yaml +11 -0
- data/spec/execution/runner_spec.rb +131 -0
- data/spec/execution/scheduled_spec.rb +29 -0
- data/spec/files/config/backup_config.rb +20 -0
- data/spec/files/data/sample.json +4 -0
- data/spec/files/data/txt/1/text1.txt +1 -0
- data/spec/files/data/txt/2/text2.txt +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/specification/configuration_spec.rb +100 -0
- data/spec/specification/easy_backup_spec.rb +34 -0
- data/spec/specification/file_system_adapter_spec.rb +39 -0
- data/spec/specification/postgre_sql_adapter_spec.rb +36 -0
- data/spec/specification/sftp_adapter_spec.rb +18 -0
- data/spec/support/helpers/config_helper.rb +7 -0
- data/spec/support/helpers/postgre_sql_helper.rb +25 -0
- metadata +175 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
## DSL Example
|
2
|
+
|
3
|
+
``` rb
|
4
|
+
EasyBackup.config do
|
5
|
+
|
6
|
+
save PostgreSQL do
|
7
|
+
host 'localhost'
|
8
|
+
port 5432
|
9
|
+
database 'db_production'
|
10
|
+
username 'user_prod'
|
11
|
+
password 'password'
|
12
|
+
zip
|
13
|
+
end
|
14
|
+
|
15
|
+
save FileSystem do
|
16
|
+
folder 'c:/resources'
|
17
|
+
file 'c:/data/file.txt'
|
18
|
+
zip 'data.zip'
|
19
|
+
end
|
20
|
+
|
21
|
+
into FileSystem do
|
22
|
+
folder lambda { "c:/backups/#{Time.now.strftime('%Y%m%d%H%M%S')}" }
|
23
|
+
end
|
24
|
+
|
25
|
+
into SFTP do
|
26
|
+
host 'remote'
|
27
|
+
username 'user_sftp'
|
28
|
+
password 'password'
|
29
|
+
folder "backups/#{Time.now.strftime('%Y%m%d%H%M%S')}"
|
30
|
+
end
|
31
|
+
|
32
|
+
every 1.day, from: 'today at 22:30'
|
33
|
+
|
34
|
+
end
|
35
|
+
```
|
data/Rakefile
ADDED
data/easy_backup.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "easy_backup/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "easy_backup"
|
7
|
+
s.version = EasyBackup::VERSION
|
8
|
+
s.authors = ["Gabriel Naiman"]
|
9
|
+
s.email = ["gabynaiman@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/gabynaiman/easy_backup"
|
11
|
+
s.summary = "Easy DSL to program backups"
|
12
|
+
s.description = "Easy DSL to program backups"
|
13
|
+
|
14
|
+
s.rubyforge_project = "easy_backup"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency 'rubyzip'
|
22
|
+
s.add_dependency 'net-sftp'
|
23
|
+
s.add_dependency 'active_support'
|
24
|
+
s.add_dependency 'i18n'
|
25
|
+
s.add_dependency 'chronic'
|
26
|
+
s.add_dependency 'rufus-scheduler'
|
27
|
+
|
28
|
+
s.add_development_dependency 'sequel'
|
29
|
+
s.add_development_dependency 'pg'
|
30
|
+
s.add_development_dependency "rspec"
|
31
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'zip/zip'
|
3
|
+
|
4
|
+
include Zip
|
5
|
+
|
6
|
+
module EasyBackup
|
7
|
+
module Adapter
|
8
|
+
module Db
|
9
|
+
class PostgreSQL
|
10
|
+
|
11
|
+
def host(host=nil)
|
12
|
+
host ? @host = host : (@host || 'localhost')
|
13
|
+
end
|
14
|
+
|
15
|
+
def database(database=nil)
|
16
|
+
database ? @database = database : @database
|
17
|
+
end
|
18
|
+
|
19
|
+
def username(username=nil)
|
20
|
+
username ? @username = username : (@username || 'postgres')
|
21
|
+
end
|
22
|
+
|
23
|
+
def password(password=nil)
|
24
|
+
password ? @password = password : @password
|
25
|
+
end
|
26
|
+
|
27
|
+
def port(port=nil)
|
28
|
+
port ? @port = port : (@port || 5432)
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_file(file_name=nil)
|
32
|
+
if file_name
|
33
|
+
@dump_file = file_name
|
34
|
+
else
|
35
|
+
if @dump_file
|
36
|
+
@dump_file.is_a?(Proc) ? @dump_file.call : @dump_file
|
37
|
+
else
|
38
|
+
"#{database}_#{Time.now.strftime('%Y%m%d%H%M%S')}.sql"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def zip_file(file_name=nil)
|
44
|
+
file_name ? @zip_file = file_name : @zip_file
|
45
|
+
end
|
46
|
+
|
47
|
+
def zip
|
48
|
+
zip_file "#{File.basename(dump_file, '.*')}.zip"
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_to(storages)
|
52
|
+
FileUtils.mkpath File.dirname(path_to(dump_file)) unless Dir.exist? File.dirname(path_to(dump_file))
|
53
|
+
|
54
|
+
EasyBackup.logger.info "[PostgreSQL] Dump postgres://#{username}:*****@#{host}:#{port}/#{database}\n#{' '*15}to #{path_to(dump_file)}"
|
55
|
+
|
56
|
+
Open3.popen3 "pg_dump -h #{host} -p #{port} -U #{username} #{database} > #{path_to(dump_file)}" do |i, o, e, t|
|
57
|
+
if t.value.success?
|
58
|
+
if zip_file
|
59
|
+
EasyBackup.logger.info "#{(' '*14)}zip #{path_to(zip_file)}"
|
60
|
+
ZipFile.open(path_to(zip_file), ZipFile::CREATE) { |zip| zip.add dump_file, path_to(dump_file) }
|
61
|
+
end
|
62
|
+
storages.each { |s| s.save path_to(zip_file ? zip_file : dump_file) }
|
63
|
+
else
|
64
|
+
EasyBackup.logger.error "[PostgreSQL] Error: #{e.readlines.join}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
FileUtils.rm path_to(dump_file) if File.exist? path_to(dump_file)
|
69
|
+
FileUtils.rm path_to(zip_file) if zip_file && File.exist?(path_to(zip_file))
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def path_to(file_name)
|
75
|
+
"#{EasyBackup.tmp_path}/pg_dump/#{file_name}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module EasyBackup
|
2
|
+
module Adapter
|
3
|
+
class FileSystem
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@folders = []
|
7
|
+
@files = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def folder(path)
|
11
|
+
@folders << path
|
12
|
+
end
|
13
|
+
|
14
|
+
def folders
|
15
|
+
@folders.map { |f| f.is_a?(Proc) ? f.call : f }
|
16
|
+
end
|
17
|
+
|
18
|
+
def file(file_name)
|
19
|
+
@files << file_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def files
|
23
|
+
@files.map { |f| f.is_a?(Proc) ? f.call : f }
|
24
|
+
end
|
25
|
+
|
26
|
+
def zip(file_name)
|
27
|
+
@zip_file = file_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def zip_file
|
31
|
+
@zip_file.is_a?(Proc) ? @zip_file.call : @zip_file
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_to(storages)
|
35
|
+
if zip_file
|
36
|
+
EasyBackup.logger.info "[FileSystem] Zip #{zip_path(zip_file)}"
|
37
|
+
FileUtils.mkpath File.dirname(zip_path(zip_file))
|
38
|
+
ZipFile.open(zip_path(zip_file), ZipFile::CREATE) do |zip|
|
39
|
+
(files | folders).each do |resource|
|
40
|
+
if Dir.exist? resource
|
41
|
+
EasyBackup.logger.info "#{' '*13}add #{resource}"
|
42
|
+
Dir["#{resource}/**/**"].each do |r|
|
43
|
+
EasyBackup.logger.info "#{' '*13}add #{r}" unless Dir.exist? r
|
44
|
+
zip.add "#{File.basename(resource)}/#{r.gsub("#{resource}/", '')}", r
|
45
|
+
end
|
46
|
+
else
|
47
|
+
EasyBackup.logger.info "#{' '*13}add #{resource}"
|
48
|
+
zip.add File.basename(resource), resource
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
storages.each { |s| s.save zip_path(zip_file) }
|
53
|
+
FileUtils.rm zip_path(zip_file)
|
54
|
+
else
|
55
|
+
(files | folders).each do |resource|
|
56
|
+
storages.each { |s| s.save resource } if File.exist? resource
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def save(resource)
|
62
|
+
folders.each do |folder|
|
63
|
+
EasyBackup.logger.info "[FileSystem] Saving #{resource}\n#{' '*15}into #{folder}"
|
64
|
+
|
65
|
+
FileUtils.mkpath folder unless Dir.exist? folder
|
66
|
+
if Dir.exist? resource
|
67
|
+
FileUtils.cp_r resource, "#{folder}/#{File.basename(resource)}"
|
68
|
+
else
|
69
|
+
FileUtils.cp resource, "#{folder}/#{File.basename(resource)}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def zip_path(file_name)
|
77
|
+
"#{EasyBackup.tmp_path}/zip/#{file_name}"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
|
3
|
+
module EasyBackup
|
4
|
+
module Adapter
|
5
|
+
class SFTP
|
6
|
+
def host(host=nil)
|
7
|
+
host ? @host = host : @host
|
8
|
+
end
|
9
|
+
|
10
|
+
def username(username=nil)
|
11
|
+
username ? @username = username : @username
|
12
|
+
end
|
13
|
+
|
14
|
+
def password(password=nil)
|
15
|
+
password ? @password = password : @password
|
16
|
+
end
|
17
|
+
|
18
|
+
def folder(folder=nil)
|
19
|
+
if folder
|
20
|
+
@folder = folder
|
21
|
+
else
|
22
|
+
@folder.is_a?(Proc) ? @folder.call : @folder
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(resource)
|
27
|
+
Net::SFTP.start(host, username, :password => password) do |sftp|
|
28
|
+
EasyBackup.logger.info "[SFTP] Saving #{resource}\n#{' '*9}into #{host} | #{username} | #{folder}"
|
29
|
+
|
30
|
+
sftp.mkpath! folder
|
31
|
+
sftp.upload! resource, "#{folder}/#{File.basename(resource)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rufus-scheduler'
|
2
|
+
|
3
|
+
module EasyBackup
|
4
|
+
|
5
|
+
class Base
|
6
|
+
|
7
|
+
def initialize(interval=EasyBackup.interval, &block)
|
8
|
+
@configurations = {}
|
9
|
+
instance_eval &block
|
10
|
+
@scheduler = Rufus::Scheduler.start_new frequency: interval
|
11
|
+
schedule
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](name)
|
15
|
+
@configurations[name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
@scheduler.join
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def []=(name, value)
|
25
|
+
@configurations[name] = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def config(name=:default, &block)
|
29
|
+
self[name] = Configuration.new do
|
30
|
+
instance_eval &block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def schedule
|
35
|
+
@configurations.each do |name, c|
|
36
|
+
c.frequencies.each do |f|
|
37
|
+
@scheduler.every f.interval, first_at: f.from do
|
38
|
+
EasyBackup.logger.info "[EasyBackup] Starting #{name} at #{Time.now}"
|
39
|
+
Runner.run c
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EasyBackup
|
2
|
+
class Configuration
|
3
|
+
attr_reader :resources, :storages, :frequencies
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@resources = []
|
7
|
+
@storages = []
|
8
|
+
@frequencies = []
|
9
|
+
instance_eval &block if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(adapter_class, &block)
|
13
|
+
resources << adapter_class.new
|
14
|
+
resources.last.instance_eval &block if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def into(adapter_class, &block)
|
18
|
+
storages << adapter_class.new
|
19
|
+
storages.last.instance_eval &block if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
def every(interval, options={})
|
23
|
+
frequencies << Frequency.new(interval, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Net
|
2
|
+
module SFTP
|
3
|
+
class Session
|
4
|
+
def mkpath!(path)
|
5
|
+
path.split('/').inject(nil) do |previous, relative|
|
6
|
+
current = previous ? "#{previous}/#{relative}" : relative
|
7
|
+
mkdir! current if dir.glob('.', current).empty?
|
8
|
+
current
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'chronic'
|
2
|
+
|
3
|
+
module EasyBackup
|
4
|
+
class Frequency
|
5
|
+
attr_accessor :interval, :from
|
6
|
+
|
7
|
+
def initialize(interval, options={})
|
8
|
+
@interval = interval
|
9
|
+
@from = options[:from].is_a?(String) ? Chronic.parse(options[:from]) : options[:from]
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
data/lib/easy_backup.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
require 'easy_backup/version'
|
5
|
+
require 'easy_backup/base'
|
6
|
+
require 'easy_backup/configuration'
|
7
|
+
require 'easy_backup/frequency'
|
8
|
+
require 'easy_backup/runner'
|
9
|
+
|
10
|
+
require 'easy_backup/adapter/file_system'
|
11
|
+
require 'easy_backup/adapter/sftp'
|
12
|
+
require 'easy_backup/adapter/db/postgre_sql'
|
13
|
+
|
14
|
+
require 'easy_backup/extension/net_sftp_session'
|
15
|
+
|
16
|
+
|
17
|
+
include EasyBackup
|
18
|
+
include EasyBackup::Adapter
|
19
|
+
include EasyBackup::Adapter::Db
|
20
|
+
|
21
|
+
module EasyBackup
|
22
|
+
|
23
|
+
def self.logger
|
24
|
+
@@logger ||= Logger.new($stdout)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.logger=(logger)
|
28
|
+
@@logger = logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.interval
|
32
|
+
@@interval ||= 1.minute
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.interval=(interval)
|
36
|
+
@@interval = interval
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.tmp_path
|
40
|
+
@tmp_path ||= "#{ENV['tmp'].gsub('\\', '/')}/easy_backup"
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.tmp_path=(path)
|
44
|
+
@tmp_path = path
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.config(name=:default, &block)
|
48
|
+
Base.new do
|
49
|
+
config name, &block
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.load(config_file)
|
54
|
+
Base.new do
|
55
|
+
eval File.open(config_file, 'r') { |f| f.readlines.join("\n") }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/spec/config.yaml
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'JSON'
|
3
|
+
require 'yaml'
|
4
|
+
require 'sequel'
|
5
|
+
require 'zip/zip'
|
6
|
+
require 'net/sftp'
|
7
|
+
|
8
|
+
describe Runner, '-> Execution' do
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
FileUtils.rm_rf BACKUP_PATH
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'Backup a file to file system' do
|
15
|
+
config = Configuration.new do
|
16
|
+
save FileSystem do
|
17
|
+
file "#{DATA_PATH}/sample.json"
|
18
|
+
end
|
19
|
+
into FileSystem do
|
20
|
+
folder "#{BACKUP_PATH}/#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Runner.run config
|
25
|
+
|
26
|
+
file = "#{config.storages.first.folders.first}/sample.json"
|
27
|
+
File.exist?(file).should be_true
|
28
|
+
data = File.open(file, 'r') { |f| JSON.parse f.readlines.join }
|
29
|
+
data['id'].should be 1234
|
30
|
+
data['name'].should eq 'sample'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Backup folder to file system' do
|
34
|
+
config = Configuration.new do
|
35
|
+
save FileSystem do
|
36
|
+
folder "#{DATA_PATH}/txt"
|
37
|
+
end
|
38
|
+
into FileSystem do
|
39
|
+
folder "#{BACKUP_PATH}/#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Runner.run config
|
44
|
+
|
45
|
+
path = "#{config.storages.first.folders.first}/txt"
|
46
|
+
(1..2).each do |i|
|
47
|
+
file = "#{path}/#{i}/text#{i}.txt"
|
48
|
+
File.exist?(file).should be_true
|
49
|
+
File.open(file, 'r') { |f| f.gets.should eq "Text file #{i}" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'Backup file and folder zipped to file system' do
|
54
|
+
zip_file = "data_#{Time.now.strftime('%Y%m%d%H%M%S%L')}.zip"
|
55
|
+
backup_path = "#{BACKUP_PATH}/#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
56
|
+
|
57
|
+
config = Configuration.new do
|
58
|
+
save FileSystem do
|
59
|
+
folder "#{DATA_PATH}/txt"
|
60
|
+
folder "#{DATA_PATH}/sample.json"
|
61
|
+
zip zip_file
|
62
|
+
end
|
63
|
+
into FileSystem do
|
64
|
+
folder backup_path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Runner.run config
|
69
|
+
|
70
|
+
File.exists?("#{backup_path}/#{zip_file}").should be_true
|
71
|
+
|
72
|
+
Zip::ZipFile.open("#{backup_path}/#{zip_file}") do |zip|
|
73
|
+
zip.find_entry('sample.json').should_not be_nil
|
74
|
+
zip.find_entry('txt/1/text1.txt').should_not be_nil
|
75
|
+
zip.find_entry('txt/2/text2.txt').should_not be_nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'Backup PostgreSQL to file system' do
|
80
|
+
PostgreSQLHelper.create_db
|
81
|
+
db = PostgreSQLHelper.configuration
|
82
|
+
backup_path = "#{BACKUP_PATH}/#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
83
|
+
|
84
|
+
config = Configuration.new do
|
85
|
+
save PostgreSQL do
|
86
|
+
host db['host']
|
87
|
+
database db['database']
|
88
|
+
port db['port']
|
89
|
+
username db['username']
|
90
|
+
password db['password']
|
91
|
+
zip
|
92
|
+
end
|
93
|
+
into FileSystem do
|
94
|
+
folder backup_path
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
Runner.run config
|
99
|
+
|
100
|
+
PostgreSQLHelper.drop_db
|
101
|
+
|
102
|
+
Dir["#{backup_path}/#{db['database']}_*.zip"].should have(1).items
|
103
|
+
Zip::ZipFile.foreach(Dir["#{backup_path}/#{db['database']}_*.zip"].first) do |entry|
|
104
|
+
entry.get_input_stream.read.should include 'PostgreSQL database dump'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'Backup a file to sftp' do
|
109
|
+
storage = ConfigHelper.get 'sftp'
|
110
|
+
backup_path = "tmp/#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
111
|
+
config = Configuration.new do
|
112
|
+
save FileSystem do
|
113
|
+
file "#{DATA_PATH}/txt/1/text1.txt"
|
114
|
+
end
|
115
|
+
into SFTP do
|
116
|
+
host storage['host']
|
117
|
+
username storage['username']
|
118
|
+
password storage['password']
|
119
|
+
folder backup_path
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Runner.run config
|
124
|
+
|
125
|
+
Net::SFTP.start(storage['host'], storage['username'], :password => storage['password']) do |sftp|
|
126
|
+
sftp.dir.glob(backup_path, 'text1.txt').should have(1).item
|
127
|
+
sftp.file.open("#{backup_path}/text1.txt", 'r') { |f| f.gets.should eq 'Text file 1' }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EasyBackup, '-> Scheduled run' do
|
4
|
+
|
5
|
+
after :all do
|
6
|
+
FileUtils.rm_rf BACKUP_PATH
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'Run scheduled backup' do
|
10
|
+
test_path = "#{BACKUP_PATH}/#{Time.now.to_f}"
|
11
|
+
|
12
|
+
EasyBackup::Base.new 0.5 do
|
13
|
+
config :test_backup do
|
14
|
+
save FileSystem do
|
15
|
+
file "#{DATA_PATH}/txt/1/text1.txt"
|
16
|
+
end
|
17
|
+
into FileSystem do
|
18
|
+
folder lambda { "#{test_path}/#{Time.now.strftime('%Y-%m-%d %H_%M_%S_%L')}" }
|
19
|
+
end
|
20
|
+
every 0.5, from: 1.second.from_now
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sleep(2)
|
25
|
+
|
26
|
+
Dir.glob("#{test_path}/*").should have(2).items
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
config :test_backup do
|
2
|
+
|
3
|
+
save PostgreSQL do
|
4
|
+
host 'localhost'
|
5
|
+
database 'test_db'
|
6
|
+
username 'user'
|
7
|
+
password 'password'
|
8
|
+
end
|
9
|
+
|
10
|
+
save FileSystem do
|
11
|
+
folder 'c:/data'
|
12
|
+
end
|
13
|
+
|
14
|
+
into FileSystem do
|
15
|
+
folder 'c:/backup'
|
16
|
+
end
|
17
|
+
|
18
|
+
every 1.day, from: 'today at 22:30'
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Text file 1
|
@@ -0,0 +1 @@
|
|
1
|
+
Text file 2
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'easy_backup'
|
4
|
+
|
5
|
+
DATA_PATH = "#{File.dirname(__FILE__)}/files/data"
|
6
|
+
BACKUP_PATH = "#{ENV['tmp'].gsub('\\', '/')}/easy_backup/backups"
|
7
|
+
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Configuration, '-> Specification' do
|
4
|
+
|
5
|
+
it 'Save file system' do
|
6
|
+
config = Configuration.new
|
7
|
+
|
8
|
+
config.resources.should be_empty
|
9
|
+
|
10
|
+
config.save FileSystem do
|
11
|
+
folder 'c:/folder1'
|
12
|
+
file 'c:/folder2/file.txt'
|
13
|
+
end
|
14
|
+
|
15
|
+
config.save FileSystem do
|
16
|
+
folder 'c:/folder3'
|
17
|
+
folder 'c:/folder4'
|
18
|
+
folder 'c:/folder5'
|
19
|
+
end
|
20
|
+
|
21
|
+
config.resources.should have(2).items
|
22
|
+
|
23
|
+
config.resources[0].should be_a FileSystem
|
24
|
+
config.resources[0].folders.should have(1).items
|
25
|
+
config.resources[0].folders.should include 'c:/folder1'
|
26
|
+
config.resources[0].files.should have(1).items
|
27
|
+
config.resources[0].files.should include 'c:/folder2/file.txt'
|
28
|
+
|
29
|
+
config.resources[1].should be_a FileSystem
|
30
|
+
config.resources[1].folders.should have(3).items
|
31
|
+
config.resources[1].folders.should include 'c:/folder3'
|
32
|
+
config.resources[1].folders.should include 'c:/folder4'
|
33
|
+
config.resources[1].folders.should include 'c:/folder5'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Save postgre sql' do
|
37
|
+
config = Configuration.new
|
38
|
+
|
39
|
+
config.resources.should be_empty
|
40
|
+
|
41
|
+
config.save PostgreSQL do
|
42
|
+
host '192.168.0.0'
|
43
|
+
database 'db0'
|
44
|
+
username 'user0'
|
45
|
+
password 'password0'
|
46
|
+
end
|
47
|
+
|
48
|
+
config.save PostgreSQL do
|
49
|
+
host '192.168.0.1'
|
50
|
+
database 'db1'
|
51
|
+
username 'user1'
|
52
|
+
password 'password1'
|
53
|
+
end
|
54
|
+
|
55
|
+
config.resources.should have(2).items
|
56
|
+
|
57
|
+
(0..1).each do |i|
|
58
|
+
config.resources[i].should be_a PostgreSQL
|
59
|
+
config.resources[i].host.should eq "192.168.0.#{i}"
|
60
|
+
config.resources[i].database.should eq "db#{i}"
|
61
|
+
config.resources[i].username.should eq "user#{i}"
|
62
|
+
config.resources[i].password.should eq "password#{i}"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'In file system' do
|
68
|
+
config = Configuration.new
|
69
|
+
|
70
|
+
config.storages.should be_empty
|
71
|
+
|
72
|
+
config.into FileSystem do
|
73
|
+
folder 'c:/backup'
|
74
|
+
end
|
75
|
+
|
76
|
+
config.into FileSystem do
|
77
|
+
folder 'c:/other_backup'
|
78
|
+
end
|
79
|
+
|
80
|
+
config.storages.should have(2).items
|
81
|
+
config.storages[0].folders[0].should eq 'c:/backup'
|
82
|
+
config.storages[1].folders[0].should eq 'c:/other_backup'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'Every day at 22:30 and weekly at 03:00' do
|
86
|
+
config = Configuration.new
|
87
|
+
|
88
|
+
config.frequencies.should be_empty
|
89
|
+
|
90
|
+
config.every 1.day, from: 'today at 22:30'
|
91
|
+
config.every 1.week, from: 'today at 03:00'
|
92
|
+
|
93
|
+
config.frequencies.should have(2).items
|
94
|
+
config.frequencies[0].interval.should eq 1.day
|
95
|
+
config.frequencies[0].from.should eq Time.local(Time.now.year, Time.now.month, Time.now.day, 22, 30)
|
96
|
+
config.frequencies[1].interval.should eq 1.week
|
97
|
+
config.frequencies[1].from.should eq Time.local(Time.now.year, Time.now.month, Time.now.day, 3)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EasyBackup, '-> Specification' do
|
4
|
+
|
5
|
+
it 'Simple configuration' do
|
6
|
+
backup = EasyBackup.config do
|
7
|
+
save PostgreSQL do
|
8
|
+
host 'localhost'
|
9
|
+
database 'test_db'
|
10
|
+
username 'user'
|
11
|
+
password 'password'
|
12
|
+
end
|
13
|
+
|
14
|
+
save FileSystem do
|
15
|
+
folder 'c:/data'
|
16
|
+
end
|
17
|
+
|
18
|
+
into FileSystem do
|
19
|
+
folder 'c:/backup'
|
20
|
+
end
|
21
|
+
|
22
|
+
every 1.day, from: 'today at 22:30'
|
23
|
+
end
|
24
|
+
|
25
|
+
backup[:default].should be_a Configuration
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'Configure from file' do
|
29
|
+
backup = EasyBackup.load "#{File.dirname(__FILE__)}/../files/config/backup_config.rb"
|
30
|
+
|
31
|
+
backup[:test_backup].should be_a Configuration
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FileSystem, '-> Specification' do
|
4
|
+
|
5
|
+
it 'Append folders' do
|
6
|
+
fs = FileSystem.new
|
7
|
+
fs.folder 'c:/folder1'
|
8
|
+
fs.folder 'c:/folder2'
|
9
|
+
|
10
|
+
fs.folders.should have(2).items
|
11
|
+
fs.folders.should include('c:/folder1')
|
12
|
+
fs.folders.should include('c:/folder2')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'Append files' do
|
16
|
+
fs = FileSystem.new
|
17
|
+
fs.file 'c:/file1.txt'
|
18
|
+
fs.file 'c:/file2.txt'
|
19
|
+
|
20
|
+
fs.files.should have(2).items
|
21
|
+
fs.files.should include('c:/file1.txt')
|
22
|
+
fs.files.should include('c:/file2.txt')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'Zip files and folders' do
|
26
|
+
fs = FileSystem.new
|
27
|
+
|
28
|
+
fs.zip_file.should be_nil
|
29
|
+
|
30
|
+
fs.zip 'data.zip'
|
31
|
+
|
32
|
+
fs.zip_file.should eq 'data.zip'
|
33
|
+
|
34
|
+
fs.zip lambda { "#{Time.now}.zip" }
|
35
|
+
|
36
|
+
fs.zip_file.should eq "#{Time.now}.zip"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PostgreSQL, '-> Specification' do
|
4
|
+
|
5
|
+
it 'Specify database parameters' do
|
6
|
+
db = PostgreSQL.new
|
7
|
+
db.host '192.168.0.1'
|
8
|
+
db.port 1234
|
9
|
+
db.database 'test_db'
|
10
|
+
db.username 'user'
|
11
|
+
db.password 'password'
|
12
|
+
db.dump_file 'db.sql'
|
13
|
+
db.zip
|
14
|
+
|
15
|
+
db.host.should eq '192.168.0.1'
|
16
|
+
db.port.should eq 1234
|
17
|
+
db.database.should eq 'test_db'
|
18
|
+
db.username.should eq 'user'
|
19
|
+
db.password.should eq 'password'
|
20
|
+
db.dump_file.should eq 'db.sql'
|
21
|
+
db.zip_file.should eq 'db.zip'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'Get database default parameters' do
|
25
|
+
db = PostgreSQL.new
|
26
|
+
|
27
|
+
db.host.should eq 'localhost'
|
28
|
+
db.port.should eq 5432
|
29
|
+
db.database.should be_nil
|
30
|
+
db.username.should eq 'postgres'
|
31
|
+
db.password.should be_nil
|
32
|
+
db.dump_file.should eq "#{db.database}_#{Time.now.strftime('%Y%m%d%H%M%S')}.sql"
|
33
|
+
db.zip_file.should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SFTP, '-> Specification' do
|
4
|
+
|
5
|
+
it 'Specify sftp parameters' do
|
6
|
+
sftp = SFTP.new
|
7
|
+
sftp.host '192.168.0.1'
|
8
|
+
sftp.username 'user'
|
9
|
+
sftp.password 'password'
|
10
|
+
sftp.folder '/backup'
|
11
|
+
|
12
|
+
sftp.host.should eq '192.168.0.1'
|
13
|
+
sftp.username.should eq 'user'
|
14
|
+
sftp.password.should eq 'password'
|
15
|
+
sftp.folder.should eq '/backup'
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PostgreSQLHelper
|
2
|
+
|
3
|
+
def self.configuration
|
4
|
+
if block_given?
|
5
|
+
yield ConfigHelper.get 'postgresql'
|
6
|
+
else
|
7
|
+
ConfigHelper.get 'postgresql'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_db
|
12
|
+
configuration do |c|
|
13
|
+
conn = Sequel.connect("postgres://#{c['username']}:#{c['password']}@#{c['host']}:#{c['port']}")
|
14
|
+
conn.run "CREATE DATABASE #{c['database']} WITH ENCODING='UTF8' CONNECTION LIMIT=-1;"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.drop_db
|
19
|
+
configuration do |c|
|
20
|
+
conn = Sequel.connect("postgres://#{c['username']}:#{c['password']}@#{c['host']}:#{c['port']}")
|
21
|
+
conn.run "DROP DATABASE #{c['database']};"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easy_backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gabriel Naiman
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rubyzip
|
16
|
+
requirement: &26248488 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *26248488
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: net-sftp
|
27
|
+
requirement: &26248236 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *26248236
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: active_support
|
38
|
+
requirement: &26247984 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *26247984
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: i18n
|
49
|
+
requirement: &26247732 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *26247732
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: chronic
|
60
|
+
requirement: &26247480 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *26247480
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rufus-scheduler
|
71
|
+
requirement: &26247228 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *26247228
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: sequel
|
82
|
+
requirement: &26246976 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *26246976
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: pg
|
93
|
+
requirement: &26246724 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *26246724
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: rspec
|
104
|
+
requirement: &26246472 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *26246472
|
113
|
+
description: Easy DSL to program backups
|
114
|
+
email:
|
115
|
+
- gabynaiman@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- .gitignore
|
121
|
+
- .rspec
|
122
|
+
- Gemfile
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- easy_backup.gemspec
|
126
|
+
- lib/easy_backup.rb
|
127
|
+
- lib/easy_backup/adapter/db/postgre_sql.rb
|
128
|
+
- lib/easy_backup/adapter/file_system.rb
|
129
|
+
- lib/easy_backup/adapter/sftp.rb
|
130
|
+
- lib/easy_backup/base.rb
|
131
|
+
- lib/easy_backup/configuration.rb
|
132
|
+
- lib/easy_backup/extension/net_sftp_session.rb
|
133
|
+
- lib/easy_backup/frequency.rb
|
134
|
+
- lib/easy_backup/runner.rb
|
135
|
+
- lib/easy_backup/version.rb
|
136
|
+
- spec/config.yaml
|
137
|
+
- spec/execution/runner_spec.rb
|
138
|
+
- spec/execution/scheduled_spec.rb
|
139
|
+
- spec/files/config/backup_config.rb
|
140
|
+
- spec/files/data/sample.json
|
141
|
+
- spec/files/data/txt/1/text1.txt
|
142
|
+
- spec/files/data/txt/2/text2.txt
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/specification/configuration_spec.rb
|
145
|
+
- spec/specification/easy_backup_spec.rb
|
146
|
+
- spec/specification/file_system_adapter_spec.rb
|
147
|
+
- spec/specification/postgre_sql_adapter_spec.rb
|
148
|
+
- spec/specification/sftp_adapter_spec.rb
|
149
|
+
- spec/support/helpers/config_helper.rb
|
150
|
+
- spec/support/helpers/postgre_sql_helper.rb
|
151
|
+
homepage: https://github.com/gabynaiman/easy_backup
|
152
|
+
licenses: []
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ! '>='
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
165
|
+
requirements:
|
166
|
+
- - ! '>='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project: easy_backup
|
171
|
+
rubygems_version: 1.8.16
|
172
|
+
signing_key:
|
173
|
+
specification_version: 3
|
174
|
+
summary: Easy DSL to program backups
|
175
|
+
test_files: []
|