poring_backup 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +34 -0
- data/lib/generators/poring_backup/install_generator.rb +13 -0
- data/lib/generators/poring_backup/templates/init_backup.rb +28 -0
- data/lib/poring_backup.rb +24 -0
- data/lib/poring_backup/callback.rb +29 -0
- data/lib/poring_backup/database.rb +44 -0
- data/lib/poring_backup/databases/postgresql.rb +81 -0
- data/lib/poring_backup/logger.rb +45 -0
- data/lib/poring_backup/railtie.rb +10 -0
- data/lib/poring_backup/setting.rb +64 -0
- data/lib/poring_backup/storage.rb +11 -0
- data/lib/poring_backup/storages/s3.rb +66 -0
- data/lib/poring_backup/version.rb +3 -0
- data/lib/tasks/poring_backup_tasks.task +14 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.sample.yml +16 -0
- data/test/dummy/config/database.travis.yml +18 -0
- data/test/dummy/config/database.yml +16 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/poring_backup.rb +23 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.sample.yml +22 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/schema.rb +19 -0
- data/test/dummy/log/backup_development.log +139 -0
- data/test/dummy/log/backup_test.log +27 -0
- data/test/dummy/log/development.log +3 -0
- data/test/dummy/log/test.log +1524 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/spec/lib/database_spec.rb +46 -0
- data/test/dummy/spec/lib/databases/postgresql_spec.rb +85 -0
- data/test/dummy/spec/lib/setting_spec.rb +62 -0
- data/test/dummy/spec/lib/storage_spec.rb +15 -0
- data/test/dummy/spec/lib/storages/s3_spec.rb +43 -0
- data/test/dummy/spec/rails_helper.rb +69 -0
- data/test/dummy/spec/spec_helper.rb +87 -0
- data/test/poring_backup_test.rb +7 -0
- data/test/test_helper.rb +19 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9443a747e4f2589aa9c81ac0d0968359070e21eb
|
4
|
+
data.tar.gz: 557092c3199de2e150dcfef3b0afabd2655e0fb1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2b3298b1c5ffb8db11edcb4919a0c2b3c422ea01cf0b01cebbff4b701d2a1b7e8a04350a1f71415ead2fa9cda8ee0ac46372dfaeeb5c62c5ed25f8414b00b3f9
|
7
|
+
data.tar.gz: 43c5429b16dde4ea30f1137d19f20a843a5a8f1028cc451833e4ec11c3c269d060eff27fe3a41d2fced98d94f76aff71a3cac859f2249dcc93f7e52429b994a3
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 PiYa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'PoringBackup'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module PoringBackup
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def copy_initializer_file
|
9
|
+
copy_file "init_backup.rb", "config/initializers/poring_backup.rb"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
# run backup `rake poring_backup:perform`
|
3
|
+
|
4
|
+
PoringBackup.config do
|
5
|
+
|
6
|
+
# before do
|
7
|
+
# #do somethings before backup
|
8
|
+
# end
|
9
|
+
|
10
|
+
# after do
|
11
|
+
# #do somethings after backup
|
12
|
+
# end
|
13
|
+
|
14
|
+
# database :PostgreSQL do
|
15
|
+
# database 'database_name'
|
16
|
+
# username 'username'
|
17
|
+
# password 'password'
|
18
|
+
# end
|
19
|
+
|
20
|
+
# store_with :S3 do
|
21
|
+
# access_key_id 'access_key_id'
|
22
|
+
# secret_access_key 'secret_access_key'
|
23
|
+
# bucket 'bucket_name'
|
24
|
+
# region 'region'
|
25
|
+
# path 'your/path'
|
26
|
+
# end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'logging'
|
4
|
+
|
5
|
+
require 'poring_backup/setting'
|
6
|
+
require 'poring_backup/logger'
|
7
|
+
|
8
|
+
require 'poring_backup/database'
|
9
|
+
require 'poring_backup/databases/postgresql'
|
10
|
+
|
11
|
+
require 'poring_backup/storage'
|
12
|
+
require 'poring_backup/storages/s3'
|
13
|
+
|
14
|
+
# tasks
|
15
|
+
require "poring_backup/railtie" if defined?(Rails)
|
16
|
+
|
17
|
+
module PoringBackup
|
18
|
+
class << self
|
19
|
+
attr_reader :model
|
20
|
+
def config &block
|
21
|
+
@model = Setting.new(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PoringBackup
|
2
|
+
module Settings
|
3
|
+
module Callback
|
4
|
+
|
5
|
+
def before &block
|
6
|
+
@before_actions << block
|
7
|
+
end
|
8
|
+
|
9
|
+
def after &block
|
10
|
+
@after_actions << block
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def before_backup
|
15
|
+
unless @before_actions.empty?
|
16
|
+
PoringBackup.logger.info "----- before processing..."
|
17
|
+
@before_actions.each { |action| action.call(PoringBackup.logger) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_backup
|
22
|
+
unless @after_actions.empty?
|
23
|
+
PoringBackup.logger.info "----- after processing..."
|
24
|
+
@after_actions.each { |action| action.call(PoringBackup.logger) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PoringBackup
|
2
|
+
class Database
|
3
|
+
|
4
|
+
attr_reader :setting
|
5
|
+
attr_reader :file, :tmp_file_path, :file_path
|
6
|
+
attr_reader :path, :tmp_dir, :file_dir
|
7
|
+
attr_reader :created_at
|
8
|
+
|
9
|
+
def initialize setting, &block
|
10
|
+
@created_at = setting.created_at
|
11
|
+
@setting = setting
|
12
|
+
@path ||= 'db_backups'
|
13
|
+
@file ||= 'db_backup.psql'
|
14
|
+
|
15
|
+
gen_file_dir
|
16
|
+
gen_file_path
|
17
|
+
gen_tmp_dir
|
18
|
+
gen_tmp_file_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def backup
|
22
|
+
FileUtils.mkdir_p(tmp_dir)
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear_tmp!
|
26
|
+
FileUtils.rm_rf(tmp_file_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def gen_file_dir
|
31
|
+
@file_dir = "#{path}/#{created_at}"
|
32
|
+
end
|
33
|
+
def gen_file_path
|
34
|
+
@file_path = "#{file_dir}/#{file}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def gen_tmp_dir
|
38
|
+
@tmp_dir = "#{setting.tmp_dir}/#{file_dir}"
|
39
|
+
end
|
40
|
+
def gen_tmp_file_path
|
41
|
+
@tmp_file_path ||= "#{tmp_dir}/#{file}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module PoringBackup
|
2
|
+
module Databases
|
3
|
+
class PostgreSQL < Database
|
4
|
+
|
5
|
+
def initialize setting, &block
|
6
|
+
@database = nil
|
7
|
+
@host = 'localhost'
|
8
|
+
@port = nil
|
9
|
+
@username = nil
|
10
|
+
@password = nil
|
11
|
+
|
12
|
+
instance_eval(&block) if block_given?
|
13
|
+
@file = "#{@database}.pgsql"
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def backup
|
18
|
+
PoringBackup.logger.info "PostgreSQL backup processing"
|
19
|
+
super
|
20
|
+
begin
|
21
|
+
system db_dump
|
22
|
+
PoringBackup.logger.info "#{' '*18}success"
|
23
|
+
rescue => e
|
24
|
+
PoringBackup.logger.warn "#{' '*18}failed!"
|
25
|
+
PoringBackup.logger.debug "#{' '*18}tmp_file: #{tmp_file_path}"
|
26
|
+
PoringBackup.logger.debug "#{' '*18}errors => #{e}"
|
27
|
+
end
|
28
|
+
PoringBackup.logger.info "#{' '*18}finished"
|
29
|
+
end
|
30
|
+
|
31
|
+
def restore
|
32
|
+
#psql -U {user-name} -d {desintation_db} -f {dumpfilename.sql}
|
33
|
+
#psql -U <username> -d <dbname> -1 -f <filename>.sql
|
34
|
+
#pg_restore -U <username> -d <dbname> -1 -f <filename>.dump
|
35
|
+
end
|
36
|
+
|
37
|
+
def database name
|
38
|
+
@database = name
|
39
|
+
end
|
40
|
+
|
41
|
+
def host name
|
42
|
+
@host = name
|
43
|
+
end
|
44
|
+
|
45
|
+
def port number
|
46
|
+
@port = number
|
47
|
+
end
|
48
|
+
|
49
|
+
def username name
|
50
|
+
@username = name
|
51
|
+
end
|
52
|
+
|
53
|
+
def password password
|
54
|
+
@password = password
|
55
|
+
end
|
56
|
+
|
57
|
+
def db_dump
|
58
|
+
"pg_dump #{connection_options} #{general_options} #{@database}"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def connection_options
|
63
|
+
options = ''
|
64
|
+
options << "--host=#{@host || 'localhost'}"
|
65
|
+
options << " --port=#{@port}" if @port
|
66
|
+
options << " --username=#{@username}" if @username
|
67
|
+
# options << " --no-password"
|
68
|
+
# options << " --password"
|
69
|
+
options
|
70
|
+
end
|
71
|
+
|
72
|
+
def general_options
|
73
|
+
options = ''
|
74
|
+
options << "--file=#{tmp_file_path}"
|
75
|
+
options << ' --compress=9'
|
76
|
+
options
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module PoringBackup
|
2
|
+
class << self
|
3
|
+
attr_reader :logger
|
4
|
+
end
|
5
|
+
module Logger
|
6
|
+
class << self
|
7
|
+
def log_file_path
|
8
|
+
file_name = Rails.env.product? ? 'backup' : "backup_#{Rails.env}"
|
9
|
+
"log/#{file_name}.log"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Logging.color_scheme( 'bright',
|
14
|
+
:levels => {
|
15
|
+
:info => :green,
|
16
|
+
:warn => :yellow,
|
17
|
+
:error => :red,
|
18
|
+
:fatal => [:white, :on_red]
|
19
|
+
},
|
20
|
+
:date => :blue,
|
21
|
+
:logger => :cyan,
|
22
|
+
:message => :magenta
|
23
|
+
)
|
24
|
+
|
25
|
+
Logging.appenders.stdout(
|
26
|
+
'stdout',
|
27
|
+
:layout => Logging.layouts.pattern(
|
28
|
+
:pattern => '[%d] %-5l %c: %m\n',
|
29
|
+
:color_scheme => 'bright'
|
30
|
+
)
|
31
|
+
)
|
32
|
+
|
33
|
+
PoringBackup.instance_variable_set("@logger", Logging.logger['PoringBackup::Log'])
|
34
|
+
PoringBackup.logger.add_appenders(
|
35
|
+
Logging.appenders.stdout,
|
36
|
+
Logging.appenders.file(:poring_backup,
|
37
|
+
filename: log_file_path,
|
38
|
+
# layout: Logging.layouts.pattern(
|
39
|
+
# :pattern => '[%d] %-5l : %m\n',
|
40
|
+
# :color_scheme => 'bright'
|
41
|
+
# )
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'poring_backup/callback'
|
2
|
+
|
3
|
+
module PoringBackup
|
4
|
+
class Setting
|
5
|
+
include Settings::Callback
|
6
|
+
|
7
|
+
attr_reader :before_actions, :after_actions
|
8
|
+
attr_reader :dir, :tmp_dir
|
9
|
+
attr_reader :databases, :storages
|
10
|
+
attr_reader :created_at
|
11
|
+
|
12
|
+
def initialize &block
|
13
|
+
@created_at = Time.now.strftime("%Y.%m.%d.%H.%M.%S")
|
14
|
+
@before_actions = []
|
15
|
+
@after_actions = []
|
16
|
+
@dir = 'poring_backups'
|
17
|
+
@tmp_dir = "tmp/poring_backups"
|
18
|
+
@databases = []
|
19
|
+
@storages = []
|
20
|
+
instance_eval(&block) if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def database model, &block
|
24
|
+
@databases << class_from_scope(Databases, model).new(self, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def store_with model, &block
|
28
|
+
@storages << class_from_scope(Storages, model).new(self, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def perform!
|
32
|
+
PoringBackup.logger.info "PoringBackup Start..."
|
33
|
+
before_backup
|
34
|
+
backup!
|
35
|
+
store!
|
36
|
+
clear_tmp!
|
37
|
+
after_backup
|
38
|
+
PoringBackup.logger.info "PoringBackup Done"
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear_tmp!
|
42
|
+
FileUtils.rm_rf(tmp_dir)
|
43
|
+
PoringBackup.logger.debug "clear tmp directory"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def class_from_scope scope, klass
|
48
|
+
"#{scope}::#{klass}".constantize
|
49
|
+
end
|
50
|
+
|
51
|
+
def backup!
|
52
|
+
PoringBackup.logger.info "--------- Backup ----------"
|
53
|
+
databases.each(&:backup)
|
54
|
+
end
|
55
|
+
|
56
|
+
def store!
|
57
|
+
PoringBackup.logger.info "----------- Store ---------"
|
58
|
+
storages.each(&:upload)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module PoringBackup
|
2
|
+
module Storages
|
3
|
+
class S3 < Storage
|
4
|
+
|
5
|
+
attr_reader :s3, :bucket_object
|
6
|
+
|
7
|
+
def initialize setting, &block
|
8
|
+
super
|
9
|
+
instance_eval(&block) if block_given?
|
10
|
+
@s3 = aws_resource
|
11
|
+
@bucket_object = @s3.bucket(@bucket)
|
12
|
+
end
|
13
|
+
|
14
|
+
def access_key_id key
|
15
|
+
@access_key_id = key
|
16
|
+
end
|
17
|
+
|
18
|
+
def secret_access_key key
|
19
|
+
@secret_access_key = key
|
20
|
+
end
|
21
|
+
|
22
|
+
def bucket name
|
23
|
+
@bucket = name
|
24
|
+
end
|
25
|
+
|
26
|
+
def region name
|
27
|
+
@region = name
|
28
|
+
end
|
29
|
+
|
30
|
+
def path name
|
31
|
+
@path = name
|
32
|
+
end
|
33
|
+
|
34
|
+
def upload
|
35
|
+
PoringBackup.logger.info "S3 processing"
|
36
|
+
setting.databases.each do |db|
|
37
|
+
s3_path = "#{@path}/#{db.file_path}"
|
38
|
+
object = @bucket_object.object(s3_path)
|
39
|
+
success = object.upload_file(db.tmp_file_path)
|
40
|
+
if success
|
41
|
+
PoringBackup.logger.info "#{' '*3}uploaded: #{s3_path}"
|
42
|
+
else
|
43
|
+
PoringBackup.logger.warn "#{' '*3}uploaded failure: #{db.tmp_file_path}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
PoringBackup.logger.info "#{' '*3}finished"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def aws_resource
|
51
|
+
@aws_resource ||= Aws::S3::Resource.new(client: aws_client)
|
52
|
+
end
|
53
|
+
def aws_client
|
54
|
+
@aws_client ||= Aws::S3::Client.new(
|
55
|
+
region: @region,
|
56
|
+
credentials: aws_credentials
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def aws_credentials
|
61
|
+
@aws_credentials ||= Aws::Credentials.new(@access_key_id, @secret_access_key)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|