easy_backup 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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