easy_backup 0.0.6 → 0.0.7

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.
data/README.md CHANGED
@@ -1,35 +1,60 @@
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
- ```
1
+ # EasyBackup
2
+
3
+ Simple DSL to program backups
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'easy_backup'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install easy_backup
18
+
19
+ ## Usage
20
+
21
+ EasyBackup::Specification.new do
22
+
23
+ save Postgres do
24
+ host 'localhost'
25
+ port 5432
26
+ database 'db_production'
27
+ username 'user_prod'
28
+ password 'password'
29
+ zip
30
+ end
31
+
32
+ save FileSystem do
33
+ folder 'c:/resources'
34
+ file 'c:/data/file.txt'
35
+ zip 'data.zip'
36
+ end
37
+
38
+ into FileSystem do
39
+ folder lambda { "c:/backups/#{Time.now.strftime('%Y%m%d%H%M%S')}" }
40
+ end
41
+
42
+ into SFTP do
43
+ host 'remote'
44
+ username 'user_sftp'
45
+ password 'password'
46
+ folder "backups/#{Time.now.strftime('%Y%m%d%H%M%S')}"
47
+ end
48
+
49
+ schedule :every, '3h', :first_at => Chronic.parse('this tuesday 5:00')
50
+
51
+ end
52
+
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create new Pull Request
data/easy_backup.gemspec CHANGED
@@ -3,15 +3,15 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
  require "easy_backup/version"
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "easy_backup"
6
+ s.name = 'easy_backup'
7
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"
8
+ s.authors = ['Gabriel Naiman']
9
+ s.email = ['gabynaiman@gmail.com']
10
+ s.homepage = 'https://github.com/gabynaiman/easy_backup'
11
+ s.summary = 'Simple DSL to program backups'
12
+ s.description = 'Simple DSL to program backups'
13
13
 
14
- s.rubyforge_project = "easy_backup"
14
+ s.rubyforge_project = 'easy_backup'
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -20,15 +20,13 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.add_dependency 'rubyzip'
22
22
  s.add_dependency 'net-sftp'
23
- s.add_dependency 'activesupport', '>= 3.0.0'
24
- s.add_dependency 'i18n'
25
- s.add_dependency 'chronic'
26
- s.add_dependency 'json'
27
23
  s.add_dependency 'rufus-scheduler'
28
- s.add_dependency 'jruby-openssl' if RUBY_ENGINE == 'jruby'
29
- s.add_dependency 'jruby-pageant' if RUBY_ENGINE == 'jruby'
24
+ #s.add_dependency 'activesupport', '>= 3.0.0'
25
+ #s.add_dependency 'i18n'
26
+ #s.add_dependency 'chronic'
30
27
 
28
+ s.add_development_dependency 'json'
31
29
  s.add_development_dependency 'sequel'
32
- s.add_development_dependency 'pg' if RUBY_ENGINE == 'ruby'
33
- s.add_development_dependency "rspec"
30
+ s.add_development_dependency 'pg'
31
+ s.add_development_dependency 'rspec'
34
32
  end
@@ -1,26 +1,20 @@
1
1
  module EasyBackup
2
2
  class Configuration
3
- attr_reader :resources, :storages, :frequencies
4
3
 
5
- def initialize(&block)
6
- @resources = []
7
- @storages = []
8
- @frequencies = []
9
- instance_eval &block if block_given?
4
+ def logger
5
+ @logger ||= Logger.new($stdout)
10
6
  end
11
7
 
12
- def save(adapter_class, &block)
13
- resources << adapter_class.new
14
- resources.last.instance_eval &block if block_given?
8
+ def logger=(logger)
9
+ @logger = logger
15
10
  end
16
11
 
17
- def into(adapter_class, &block)
18
- storages << adapter_class.new
19
- storages.last.instance_eval &block if block_given?
12
+ def tmp_path
13
+ @tmp_path ||= "#{ENV['tmp'].gsub('\\', '/')}/easy_backup"
20
14
  end
21
15
 
22
- def every(interval, options={})
23
- frequencies << Frequency.new(interval, options)
16
+ def tmp_path=(path)
17
+ @tmp_path = path
24
18
  end
25
19
 
26
20
  end
@@ -1,10 +1,12 @@
1
1
  module EasyBackup
2
- module Adapter
2
+ module Resources
3
3
  class FileSystem
4
+ include Zip
4
5
 
5
- def initialize
6
+ def initialize(&block)
6
7
  @folders = []
7
8
  @files = []
9
+ instance_eval &block if block_given?
8
10
  end
9
11
 
10
12
  def folder(path)
@@ -28,31 +30,42 @@ module EasyBackup
28
30
  end
29
31
 
30
32
  def zip_file
31
- @zip_file.is_a?(Proc) ? @zip_file.call : @zip_file
33
+ zip_path(@zip_file.is_a?(Proc) ? @zip_file.call : @zip_file)
32
34
  end
33
35
 
34
- def send_to(storages)
35
- if zip_file
36
- zip_file_name = zip_path(zip_file)
36
+ def save(resource)
37
+ folders.each do |folder|
38
+ EasyBackup.configuration.logger.info "[EasyBackup] Saving #{resource} into #{folder}"
37
39
 
38
- EasyBackup.logger.info "[FileSystem] Zip #{zip_file_name}"
39
- FileUtils.mkpath File.dirname(zip_file_name)
40
- ZipFile.open(zip_file_name, ZipFile::CREATE) do |zip|
40
+ FileUtils.mkpath folder unless Dir.exist? folder
41
+ if Dir.exist? resource
42
+ FileUtils.cp_r resource, "#{folder}/#{File.basename(resource)}"
43
+ else
44
+ FileUtils.cp resource, "#{folder}/#{File.basename(resource)}"
45
+ end
46
+ end
47
+ end
48
+
49
+ def send_to(*storages)
50
+ if zip_file
51
+ EasyBackup.configuration.logger.info "[EasyBackup] Zip #{zip_file}"
52
+ FileUtils.mkpath File.dirname(zip_file) unless Dir.exist? File.dirname(zip_file)
53
+ ZipFile.open(zip_file, ZipFile::CREATE) do |zip|
41
54
  (files | folders).each do |resource|
42
55
  if Dir.exist? resource
43
- EasyBackup.logger.debug "#{' '*13}add #{resource}"
56
+ EasyBackup.configuration.logger.debug "[EasyBackup] add #{resource}"
44
57
  Dir.glob("#{resource}/**/**", File::FNM_DOTMATCH).reject{|entry| entry =~ /\/[\.]+\z/}.each do |r|
45
- EasyBackup.logger.debug "#{' '*13}add #{r}" unless Dir.exist? r
58
+ EasyBackup.configuration.logger.debug "[EasyBackup] add #{r}" unless Dir.exist? r
46
59
  zip.add "#{File.basename(resource)}/#{r.gsub("#{resource}/", '')}", r
47
60
  end
48
61
  else
49
- EasyBackup.logger.debug "#{' '*13}add #{resource}"
62
+ EasyBackup.configuration.logger.debug "[EasyBackup] add #{resource}"
50
63
  zip.add File.basename(resource), resource
51
64
  end
52
65
  end
53
66
  end
54
- storages.each { |s| s.save zip_file_name }
55
- FileUtils.rm zip_file_name
67
+ storages.each { |s| s.save zip_file }
68
+ FileUtils.rm zip_file
56
69
  else
57
70
  (files | folders).each do |resource|
58
71
  storages.each { |s| s.save resource } if File.exist? resource
@@ -60,26 +73,13 @@ module EasyBackup
60
73
  end
61
74
  end
62
75
 
63
- def save(resource)
64
- folders.each do |folder|
65
- EasyBackup.logger.info "[FileSystem] Saving #{resource}\n#{' '*15}into #{folder}"
66
-
67
- FileUtils.mkpath folder unless Dir.exist? folder
68
- if Dir.exist? resource
69
- FileUtils.cp_r resource, "#{folder}/#{File.basename(resource)}"
70
- else
71
- FileUtils.cp resource, "#{folder}/#{File.basename(resource)}"
72
- end
73
- end
74
- end
75
-
76
76
  private
77
77
 
78
78
  def zip_path(file_name)
79
- "#{EasyBackup.tmp_path}/zip/#{file_name}"
79
+ return nil unless file_name
80
+ "#{EasyBackup.configuration.tmp_path}/zip/#{file_name}"
80
81
  end
81
82
 
82
83
  end
83
84
  end
84
- end
85
-
85
+ end
@@ -0,0 +1,86 @@
1
+ module EasyBackup
2
+ module Resources
3
+ class Postgres
4
+ include Zip
5
+
6
+ def initialize(&block)
7
+ instance_eval &block if block_given?
8
+ end
9
+
10
+ def host(host=nil)
11
+ host ? @host = host : (@host || 'localhost')
12
+ end
13
+
14
+ def database(database=nil)
15
+ database ? @database = database : @database
16
+ end
17
+
18
+ def username(username=nil)
19
+ username ? @username = username : (@username || 'postgres')
20
+ end
21
+
22
+ def password(password=nil)
23
+ password ? @password = password : @password
24
+ end
25
+
26
+ def port(port=nil)
27
+ port ? @port = port : (@port || 5432)
28
+ end
29
+
30
+ def dump_file(file_name=nil)
31
+ if file_name
32
+ @dump_file = file_name
33
+ else
34
+ if @dump_file
35
+ path_to(@dump_file.is_a?(Proc) ? @dump_file.call : @dump_file)
36
+ else
37
+ path_to "#{database}_#{Time.now.strftime('%Y%m%d%H%M%S')}.sql"
38
+ end
39
+ end
40
+ end
41
+
42
+ def zip_file(file_name=nil)
43
+ if file_name
44
+ @zip_file = file_name
45
+ else
46
+ path_to(@zip_file.is_a?(Proc) ? @zip_file.call : @zip_file)
47
+ end
48
+ end
49
+
50
+ def zip
51
+ zip_file lambda { "#{File.basename(dump_file, '.*')}.zip" }
52
+ end
53
+
54
+ def send_to(*storages)
55
+ FileUtils.mkpath File.dirname(dump_file) unless Dir.exist? File.dirname(dump_file)
56
+
57
+ EasyBackup.configuration.logger.info "[EasyBackup] Dump postgres://#{username}:*****@#{host}:#{port}/#{database} to #{dump_file}"
58
+
59
+ Open3.popen3 "pg_dump -h #{host} -p #{port} -U #{username} #{database} > #{dump_file}" do |i, o, e, t|
60
+ if t.value.success?
61
+ if zip_file
62
+ EasyBackup.configuration.logger.info "[EasyBackup] Zip #{dump_file} to #{zip_file}"
63
+ ZipFile.open(zip_file, ZipFile::CREATE) do |zip|
64
+ zip.add File.basename(dump_file), dump_file
65
+ end
66
+ end
67
+ storages.each { |s| s.save(zip_file ? zip_file : dump_file) }
68
+ else
69
+ EasyBackup.configuration.logger.error "[EasyBackup] Error: #{e.readlines.join}"
70
+ end
71
+ end
72
+
73
+ FileUtils.rm dump_file if File.exist? dump_file
74
+ FileUtils.rm zip_file if zip_file && File.exist?(zip_file)
75
+ end
76
+
77
+ private
78
+
79
+ def path_to(file_name)
80
+ return nil unless file_name
81
+ "#{EasyBackup.configuration.tmp_path}/pg_dump/#{file_name}"
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -1,8 +1,11 @@
1
- require 'net/sftp'
2
-
3
1
  module EasyBackup
4
- module Adapter
2
+ module Resources
5
3
  class SFTP
4
+
5
+ def initialize(&block)
6
+ instance_eval &block if block_given?
7
+ end
8
+
6
9
  def host(host=nil)
7
10
  host ? @host = host : @host
8
11
  end
@@ -26,7 +29,7 @@ module EasyBackup
26
29
  def save(resource)
27
30
  Net::SFTP.start(host, username, :password => password) do |sftp|
28
31
  sftp_folder = folder
29
- EasyBackup.logger.info "[SFTP] Saving #{resource}\n#{' '*9}into #{host} | #{username} | #{sftp_folder}"
32
+ EasyBackup.configuration.logger.info "[EasyBackup] Saving #{resource} into #{host} | #{username} | #{sftp_folder}"
30
33
 
31
34
  sftp.mkpath! sftp_folder, :progress => SFTPHandler.new
32
35
  sftp.upload! resource, "#{sftp_folder}/#{File.basename(resource)}", :progress => SFTPHandler.new
@@ -36,24 +39,25 @@ module EasyBackup
36
39
 
37
40
  class SFTPHandler
38
41
  def on_open(uploader, file)
39
- EasyBackup.logger.info "#{' '*9}starting upload: #{file.local} -> #{file.remote} (#{file.size} bytes)"
42
+ EasyBackup.configuration.logger.debug "[EasyBackup] starting upload: #{file.local} -> #{file.remote} (#{file.size} bytes)"
40
43
  end
41
44
 
42
45
  def on_put(uploader, file, offset, data)
43
- EasyBackup.logger.info "#{' '*9}#{data.length} bytes to #{file.remote} starting at #{offset}"
46
+ EasyBackup.configuration.logger.debug "[EasyBackup] #{data.length} bytes to #{file.remote} starting at #{offset}"
44
47
  end
45
48
 
46
49
  def on_close(uploader, file)
47
- EasyBackup.logger.info "#{' '*9}finished with #{file.remote}"
50
+ EasyBackup.configuration.logger.debug "[EasyBackup] finished with #{file.remote}"
48
51
  end
49
52
 
50
53
  def on_mkdir(uploader, path)
51
- EasyBackup.logger.info "#{' '*9}creating directory #{path}"
54
+ EasyBackup.configuration.logger.debug "[EasyBackup] creating directory #{path}"
52
55
  end
53
56
 
54
57
  def on_finish(uploader)
55
- EasyBackup.logger.info "#{' '*9}all done!"
58
+ EasyBackup.configuration.logger.debug "[EasyBackup] all done!"
56
59
  end
57
60
  end
61
+
58
62
  end
59
- end
63
+ end
@@ -0,0 +1,36 @@
1
+ module EasyBackup
2
+ class Specification
3
+
4
+ def initialize(&block)
5
+ @resources = []
6
+ @storages = []
7
+ instance_eval &block if block_given?
8
+ end
9
+
10
+ def save(resource_class, &block)
11
+ raise "#{resource_class} not respond to send_to" unless resource_class.instance_methods.include? :send_to
12
+ @resources << resource_class.new
13
+ @resources.last.instance_eval &block if block_given?
14
+ end
15
+
16
+ def into(resource_class, &block)
17
+ raise "#{resource_class} not respond to save" unless resource_class.instance_methods.include? :save
18
+ @storages << resource_class.new
19
+ @storages.last.instance_eval &block if block_given?
20
+ end
21
+
22
+ def schedule(frequency, *args)
23
+ EasyBackup.scheduler.send(frequency, *args) do
24
+ EasyBackup.configuration.logger.info "[EasyBackup] Starting at #{Time.now}"
25
+ run
26
+ end
27
+ end
28
+
29
+ def run
30
+ @resources.each do |resource|
31
+ resource.send_to *@storages
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module EasyBackup
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
data/lib/easy_backup.rb CHANGED
@@ -1,59 +1,30 @@
1
1
  require 'logger'
2
- require 'active_support/all'
2
+ require 'zip/zip'
3
+ require 'net/sftp'
4
+ require 'open3'
5
+ require 'rufus-scheduler'
3
6
 
4
- require 'easy_backup/version'
5
- require 'easy_backup/base'
6
7
  require 'easy_backup/configuration'
7
- require 'easy_backup/frequency'
8
- require 'easy_backup/runner'
8
+ require 'easy_backup/specification'
9
9
 
10
- require 'easy_backup/adapter/file_system'
11
- require 'easy_backup/adapter/sftp'
12
- require 'easy_backup/adapter/db/postgre_sql'
10
+ require 'easy_backup/resources/file_system'
11
+ require 'easy_backup/resources/sftp'
12
+ require 'easy_backup/resources/postgres'
13
13
 
14
14
  require 'easy_backup/extension/net_sftp_session'
15
15
 
16
-
17
- include EasyBackup
18
- include EasyBackup::Adapter
19
- include EasyBackup::Adapter::Db
20
-
21
16
  module EasyBackup
22
17
 
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
18
+ def self.configuration
19
+ @@configuration ||= Configuration.new
45
20
  end
46
21
 
47
- def self.config(name=:default, &block)
48
- Base.new do
49
- config name, &block
50
- end
22
+ def self.configure
23
+ yield(configuration)
51
24
  end
52
25
 
53
- def self.load(config_file)
54
- Base.new do
55
- eval File.open(config_file, 'r') { |f| f.readlines.join("\n") }
56
- end
26
+ def scheduler
27
+ @@scheduler ||= Rufus::Scheduler.start_new
57
28
  end
58
29
 
59
30
  end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ describe FileSystem do
4
+
5
+ context 'Specification' do
6
+
7
+ it 'Append folders' do
8
+ fs = FileSystem.new do
9
+ folder 'c:/folder1'
10
+ folder 'c:/folder2'
11
+ end
12
+
13
+ fs.folders.should have(2).items
14
+ fs.folders.should include('c:/folder1')
15
+ fs.folders.should include('c:/folder2')
16
+ end
17
+
18
+ it 'Append files' do
19
+ fs = FileSystem.new do
20
+ file 'c:/file1.txt'
21
+ file 'c:/file2.txt'
22
+ end
23
+
24
+ fs.files.should have(2).items
25
+ fs.files.should include('c:/file1.txt')
26
+ fs.files.should include('c:/file2.txt')
27
+ end
28
+
29
+ it 'Zip empty' do
30
+ fs = FileSystem.new
31
+ fs.zip_file.should be_nil
32
+ end
33
+
34
+ it 'Zip from string' do
35
+ fs = FileSystem.new { zip 'data.zip' }
36
+ fs.zip_file.should eq "#{EasyBackup.configuration.tmp_path}/zip/data.zip"
37
+ end
38
+
39
+ it 'Zip from lambda' do
40
+ fs = FileSystem.new { zip lambda { "#{Time.now}.zip" } }
41
+ fs.zip_file.should eq "#{EasyBackup.configuration.tmp_path}/zip/#{Time.now}.zip"
42
+ end
43
+
44
+ end
45
+
46
+ context 'Save' do
47
+
48
+ it 'File into specified folder' do
49
+ source_file = "#{DATA_PATH}/sample.json"
50
+ target_folder = file_helper.create_temp_folder
51
+
52
+ fs = FileSystem.new do
53
+ folder target_folder
54
+ end
55
+
56
+ File.exists?("#{target_folder}/sample.json").should be_false
57
+
58
+ fs.save source_file
59
+
60
+ File.exists?("#{target_folder}/#{File.basename(source_file)}").should be_true
61
+ data = File.open(source_file, 'r') { |f| JSON.parse f.readlines.join }
62
+ data['id'].should eq 1234
63
+ data['name'].should eq 'sample'
64
+ end
65
+
66
+ it 'Folder into specified folder' do
67
+ source_folder = "#{DATA_PATH}/txt"
68
+ target_folder = file_helper.create_temp_folder
69
+
70
+ fs = FileSystem.new do
71
+ folder target_folder
72
+ end
73
+
74
+ fs.save source_folder
75
+
76
+ 1.upto(2) do |i|
77
+ file = "#{source_folder}/#{i}/text#{i}.txt"
78
+ File.exist?(file).should be_true
79
+ File.open(file, 'r') { |f| f.gets.should eq "Text file #{i}" }
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ context 'Send' do
86
+
87
+ it 'File to storage' do
88
+ sample_file = "#{DATA_PATH}/sample.json"
89
+
90
+ fs = FileSystem.new do
91
+ file sample_file
92
+ end
93
+
94
+ storage = FakeStorage.new
95
+
96
+ fs.send_to storage
97
+
98
+ storage.received.should eq [sample_file]
99
+ end
100
+
101
+ it 'File and folder to storage' do
102
+ sample_file = "#{DATA_PATH}/sample.json"
103
+ sample_folder = "#{DATA_PATH}/txt"
104
+
105
+ fs = FileSystem.new do
106
+ file sample_file
107
+ folder sample_folder
108
+ end
109
+
110
+ storage = FakeStorage.new
111
+
112
+ fs.send_to storage
113
+
114
+ storage.received.should eq [sample_file, sample_folder]
115
+ end
116
+
117
+ it 'Zipped file to storage' do
118
+ sample_file = "#{DATA_PATH}/sample.json"
119
+ sample_folder = "#{DATA_PATH}/txt"
120
+
121
+ fs = FileSystem.new do
122
+ file sample_file
123
+ folder sample_folder
124
+ zip 'sample.zip'
125
+ end
126
+
127
+ storage = FakeStorage.new
128
+
129
+ fs.send_to storage
130
+
131
+ storage.received.should eq [fs.zip_file]
132
+ end
133
+
134
+ end
135
+
136
+ end